drawinghelper.c 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. /**
  2. * Code Taken from the foreign drawing demo of gtk
  3. * <gtk-git-repo>/demos/gtk-demo/foreigndrawing.c
  4. * License: LGPL
  5. */
  6. #include <gtk/gtk.h>
  7. #include <string.h>
  8. #include <stdio.h>
  9. void append_element (GtkWidgetPath *path, const char *selector);
  10. void
  11. query_size (GtkStyleContext *context,
  12. gint *width,
  13. gint *height)
  14. {
  15. GtkBorder margin, border, padding;
  16. int min_width, min_height;
  17. gtk_style_context_get_margin (context, gtk_style_context_get_state (context), &margin);
  18. gtk_style_context_get_border (context, gtk_style_context_get_state (context), &border);
  19. gtk_style_context_get_padding (context, gtk_style_context_get_state (context), &padding);
  20. gtk_style_context_get (context, gtk_style_context_get_state (context),
  21. "min-width", &min_width,
  22. "min-height", &min_height,
  23. NULL);
  24. min_width += margin.left + margin.right + border.left + border.right + padding.left + padding.right;
  25. min_height += margin.top + margin.bottom + border.top + border.bottom + padding.top + padding.bottom;
  26. if (width)
  27. *width = MAX (*width, min_width);
  28. if (height)
  29. *height = MAX (*height, min_height);
  30. }
  31. static GtkStyleContext *
  32. create_context_for_path (GtkWidgetPath *path,
  33. GtkStyleContext *parent)
  34. {
  35. GtkStyleContext *context;
  36. context = gtk_style_context_new ();
  37. gtk_style_context_set_path (context, path);
  38. gtk_style_context_set_parent (context, parent);
  39. /* Unfortunately, we have to explicitly set the state again here
  40. * for it to take effect
  41. */
  42. gtk_style_context_set_state (context, gtk_widget_path_iter_get_state (path, -1));
  43. gtk_widget_path_unref (path);
  44. return context;
  45. }
  46. GtkStyleContext *
  47. get_style (GtkStyleContext *parent,
  48. const char *selector)
  49. {
  50. GtkWidgetPath *path;
  51. if (parent)
  52. path = gtk_widget_path_copy (gtk_style_context_get_path (parent));
  53. else
  54. path = gtk_widget_path_new ();
  55. append_element (path, selector);
  56. return create_context_for_path (path, parent);
  57. }
  58. static void
  59. draw_style_common (GtkStyleContext *context,
  60. cairo_t *cr,
  61. gint x,
  62. gint y,
  63. gint width,
  64. gint height,
  65. gint *contents_x,
  66. gint *contents_y,
  67. gint *contents_width,
  68. gint *contents_height)
  69. {
  70. GtkBorder margin, border, padding;
  71. int min_width, min_height;
  72. gtk_style_context_get_margin (context, gtk_style_context_get_state (context), &margin);
  73. gtk_style_context_get_border (context, gtk_style_context_get_state (context), &border);
  74. gtk_style_context_get_padding (context, gtk_style_context_get_state (context), &padding);
  75. gtk_style_context_get (context, gtk_style_context_get_state (context),
  76. "min-width", &min_width,
  77. "min-height", &min_height,
  78. NULL);
  79. x += margin.left;
  80. y += margin.top;
  81. width -= margin.left + margin.right;
  82. height -= margin.top + margin.bottom;
  83. width = MAX (width, min_width);
  84. height = MAX (height, min_height);
  85. gtk_render_background (context, cr, x, y, width, height);
  86. gtk_render_frame (context, cr, x, y, width, height);
  87. if (contents_x)
  88. *contents_x = x + border.left + padding.left;
  89. if (contents_y)
  90. *contents_y = y + border.top + padding.top;
  91. if (contents_width)
  92. *contents_width = width - border.left - border.right - padding.left - padding.right;
  93. if (contents_height)
  94. *contents_height = height - border.top - border.bottom - padding.top - padding.bottom;
  95. }
  96. void
  97. gtk_flow_draw_radio (GtkWidget *widget,
  98. cairo_t *cr,
  99. gint x,
  100. gint y,
  101. GtkStateFlags state,
  102. gint *width,
  103. gint *height)
  104. {
  105. GtkStyleContext *button_context;
  106. GtkStyleContext *check_context;
  107. gint contents_x, contents_y, contents_width, contents_height;
  108. /* This information is taken from the GtkRadioButton docs, see "CSS nodes" */
  109. button_context = get_style (NULL, "radiobutton");
  110. check_context = get_style (button_context, "radio");
  111. gtk_style_context_set_state (check_context, state);
  112. *width = *height = 0;
  113. query_size (button_context, width, height);
  114. query_size (check_context, width, height);
  115. draw_style_common (button_context, cr, x, y, *width, *height, NULL, NULL, NULL, NULL);
  116. draw_style_common (check_context, cr, x, y, *width, *height,
  117. &contents_x, &contents_y, &contents_width, &contents_height);
  118. gtk_render_check (check_context, cr, contents_x, contents_y, contents_width, contents_height);
  119. g_object_unref (check_context);
  120. g_object_unref (button_context);
  121. }
  122. void
  123. gtk_flow_draw_rubberband (GtkWidget *widget,
  124. cairo_t *cr,
  125. gint x,
  126. gint y,
  127. GtkStateFlags state,
  128. gint *width,
  129. gint *height)
  130. {
  131. GtkStyleContext *rubberband_context;
  132. /* This information is taken from the GtkRadioButton docs, see "CSS nodes" */
  133. rubberband_context = get_style (NULL, "flowbox.rubberband");
  134. //*width = *height = 0;
  135. query_size (rubberband_context, width, height);
  136. draw_style_common (rubberband_context, cr, x, y, *width, *height, NULL, NULL, NULL, NULL);
  137. g_object_unref (rubberband_context);
  138. }
  139. void
  140. append_element (GtkWidgetPath *path,
  141. const char *selector)
  142. {
  143. static const struct {
  144. const char *name;
  145. GtkStateFlags state_flag;
  146. } pseudo_classes[] = {
  147. { "active", GTK_STATE_FLAG_ACTIVE },
  148. { "hover", GTK_STATE_FLAG_PRELIGHT },
  149. { "selected", GTK_STATE_FLAG_SELECTED },
  150. { "disabled", GTK_STATE_FLAG_INSENSITIVE },
  151. { "indeterminate", GTK_STATE_FLAG_INCONSISTENT },
  152. { "focus", GTK_STATE_FLAG_FOCUSED },
  153. { "backdrop", GTK_STATE_FLAG_BACKDROP },
  154. { "dir(ltr)", GTK_STATE_FLAG_DIR_LTR },
  155. { "dir(rtl)", GTK_STATE_FLAG_DIR_RTL },
  156. { "link", GTK_STATE_FLAG_LINK },
  157. { "visited", GTK_STATE_FLAG_VISITED },
  158. { "checked", GTK_STATE_FLAG_CHECKED },
  159. { "drop(active)", GTK_STATE_FLAG_DROP_ACTIVE }
  160. };
  161. const char *next;
  162. char *name;
  163. char type;
  164. guint i;
  165. next = strpbrk (selector, "#.:");
  166. if (next == NULL)
  167. next = selector + strlen (selector);
  168. name = g_strndup (selector, next - selector);
  169. if (g_ascii_isupper (selector[0]))
  170. {
  171. GType gtype;
  172. gtype = g_type_from_name (name);
  173. if (gtype == G_TYPE_INVALID)
  174. {
  175. g_critical ("Unknown type name `%s'", name);
  176. g_free (name);
  177. return;
  178. }
  179. gtk_widget_path_append_type (path, gtype);
  180. }
  181. else
  182. {
  183. /* Omit type, we're using name */
  184. gtk_widget_path_append_type (path, G_TYPE_NONE);
  185. gtk_widget_path_iter_set_object_name (path, -1, name);
  186. }
  187. g_free (name);
  188. while (*next != '\0')
  189. {
  190. type = *next;
  191. selector = next + 1;
  192. next = strpbrk (selector, "#.:");
  193. if (next == NULL)
  194. next = selector + strlen (selector);
  195. name = g_strndup (selector, next - selector);
  196. switch (type)
  197. {
  198. case '#':
  199. gtk_widget_path_iter_set_name (path, -1, name);
  200. break;
  201. case '.':
  202. gtk_widget_path_iter_add_class (path, -1, name);
  203. break;
  204. case ':':
  205. for (i = 0; i < G_N_ELEMENTS (pseudo_classes); i++)
  206. {
  207. if (g_str_equal (pseudo_classes[i].name, name))
  208. {
  209. gtk_widget_path_iter_set_state (path,
  210. -1,
  211. gtk_widget_path_iter_get_state (path, -1)
  212. | pseudo_classes[i].state_flag);
  213. break;
  214. }
  215. }
  216. if (i == G_N_ELEMENTS (pseudo_classes))
  217. g_critical ("Unknown pseudo-class :%s", name);
  218. break;
  219. default:
  220. g_assert_not_reached ();
  221. break;
  222. }
  223. g_free (name);
  224. }
  225. }