m2gtkshim.c 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. /*
  2. * m2gtkshim.c - small C support library for the m2-GTK4 Modula-2 bindings.
  3. *
  4. * GNU Modula-2 cannot register GObject subclasses (no class_init /
  5. * vfunc overrides), which blocks widgets and paintables that need a
  6. * custom snapshot. This shim provides two such types whose snapshot
  7. * vfuncs hand a Cairo context to a Modula-2 callback, so the drawing
  8. * itself reuses the existing Cairo binding:
  9. *
  10. * M2GtkWidget - a GtkWidget subclass; the M2 callback is
  11. * void draw(GtkWidget *w, cairo_t *cr,
  12. * int width, int height, gpointer user);
  13. * M2Paintable - a GdkPaintable implementation; the M2 callback is
  14. * void draw(cairo_t *cr, int width, int height,
  15. * gpointer user);
  16. *
  17. * The M2 callback addresses and user data are supplied at construction;
  18. * an optional destroy notify frees the user data.
  19. */
  20. #include <gtk/gtk.h>
  21. /* ------------------------------------------------------------------ */
  22. /* M2GtkWidget */
  23. /* ------------------------------------------------------------------ */
  24. typedef void (*M2WidgetDrawFunc) (GtkWidget *widget, cairo_t *cr,
  25. int width, int height, gpointer user_data);
  26. typedef struct _M2GtkWidget M2GtkWidget;
  27. typedef struct _M2GtkWidgetClass M2GtkWidgetClass;
  28. struct _M2GtkWidget
  29. {
  30. GtkWidget parent_instance;
  31. M2WidgetDrawFunc draw;
  32. gpointer user_data;
  33. GDestroyNotify destroy;
  34. };
  35. struct _M2GtkWidgetClass
  36. {
  37. GtkWidgetClass parent_class;
  38. };
  39. G_DEFINE_TYPE (M2GtkWidget, m2_gtk_widget, GTK_TYPE_WIDGET)
  40. static void
  41. m2_gtk_widget_snapshot (GtkWidget *widget, GtkSnapshot *snapshot)
  42. {
  43. M2GtkWidget *self = (M2GtkWidget *) widget;
  44. int width = gtk_widget_get_width (widget);
  45. int height = gtk_widget_get_height (widget);
  46. if (self->draw == NULL || width <= 0 || height <= 0)
  47. return;
  48. graphene_rect_t bounds = GRAPHENE_RECT_INIT (0.f, 0.f, width, height);
  49. cairo_t *cr = gtk_snapshot_append_cairo (snapshot, &bounds);
  50. self->draw (widget, cr, width, height, self->user_data);
  51. cairo_destroy (cr);
  52. }
  53. static void
  54. m2_gtk_widget_finalize (GObject *object)
  55. {
  56. M2GtkWidget *self = (M2GtkWidget *) object;
  57. if (self->destroy != NULL)
  58. self->destroy (self->user_data);
  59. G_OBJECT_CLASS (m2_gtk_widget_parent_class)->finalize (object);
  60. }
  61. static void
  62. m2_gtk_widget_class_init (M2GtkWidgetClass *klass)
  63. {
  64. GObjectClass *object_class = G_OBJECT_CLASS (klass);
  65. GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
  66. object_class->finalize = m2_gtk_widget_finalize;
  67. widget_class->snapshot = m2_gtk_widget_snapshot;
  68. }
  69. static void
  70. m2_gtk_widget_init (M2GtkWidget *self)
  71. {
  72. self->draw = NULL;
  73. self->user_data = NULL;
  74. self->destroy = NULL;
  75. }
  76. GtkWidget *
  77. m2_gtk_widget_new (M2WidgetDrawFunc draw, gpointer user_data,
  78. GDestroyNotify destroy)
  79. {
  80. M2GtkWidget *self = g_object_new (m2_gtk_widget_get_type (), NULL);
  81. self->draw = draw;
  82. self->user_data = user_data;
  83. self->destroy = destroy;
  84. return GTK_WIDGET (self);
  85. }
  86. /* ------------------------------------------------------------------ */
  87. /* M2Paintable (implements GdkPaintable) */
  88. /* ------------------------------------------------------------------ */
  89. typedef void (*M2PaintableDrawFunc) (cairo_t *cr, int width, int height,
  90. gpointer user_data);
  91. typedef struct _M2Paintable M2Paintable;
  92. typedef struct _M2PaintableClass M2PaintableClass;
  93. struct _M2Paintable
  94. {
  95. GObject parent_instance;
  96. M2PaintableDrawFunc draw;
  97. gpointer user_data;
  98. GDestroyNotify destroy;
  99. int width;
  100. int height;
  101. };
  102. struct _M2PaintableClass
  103. {
  104. GObjectClass parent_class;
  105. };
  106. static void
  107. m2_paintable_snapshot (GdkPaintable *paintable, GtkSnapshot *snapshot,
  108. double width, double height)
  109. {
  110. M2Paintable *self = (M2Paintable *) paintable;
  111. graphene_rect_t bounds = GRAPHENE_RECT_INIT (0.f, 0.f, (float) width,
  112. (float) height);
  113. cairo_t *cr = gtk_snapshot_append_cairo (snapshot, &bounds);
  114. if (self->draw != NULL)
  115. self->draw (cr, (int) width, (int) height, self->user_data);
  116. cairo_destroy (cr);
  117. }
  118. static int
  119. m2_paintable_get_intrinsic_width (GdkPaintable *paintable)
  120. {
  121. return ((M2Paintable *) paintable)->width;
  122. }
  123. static int
  124. m2_paintable_get_intrinsic_height (GdkPaintable *paintable)
  125. {
  126. return ((M2Paintable *) paintable)->height;
  127. }
  128. static double
  129. m2_paintable_get_intrinsic_aspect_ratio (GdkPaintable *paintable)
  130. {
  131. M2Paintable *self = (M2Paintable *) paintable;
  132. return self->height > 0 ? (double) self->width / (double) self->height : 0.0;
  133. }
  134. static void
  135. m2_paintable_paintable_init (GdkPaintableInterface *iface)
  136. {
  137. iface->snapshot = m2_paintable_snapshot;
  138. iface->get_intrinsic_width = m2_paintable_get_intrinsic_width;
  139. iface->get_intrinsic_height = m2_paintable_get_intrinsic_height;
  140. iface->get_intrinsic_aspect_ratio = m2_paintable_get_intrinsic_aspect_ratio;
  141. }
  142. G_DEFINE_TYPE_WITH_CODE (M2Paintable, m2_paintable, G_TYPE_OBJECT,
  143. G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
  144. m2_paintable_paintable_init))
  145. static void
  146. m2_paintable_finalize (GObject *object)
  147. {
  148. M2Paintable *self = (M2Paintable *) object;
  149. if (self->destroy != NULL)
  150. self->destroy (self->user_data);
  151. G_OBJECT_CLASS (m2_paintable_parent_class)->finalize (object);
  152. }
  153. static void
  154. m2_paintable_class_init (M2PaintableClass *klass)
  155. {
  156. G_OBJECT_CLASS (klass)->finalize = m2_paintable_finalize;
  157. }
  158. static void
  159. m2_paintable_init (M2Paintable *self)
  160. {
  161. self->draw = NULL;
  162. self->user_data = NULL;
  163. self->destroy = NULL;
  164. self->width = 0;
  165. self->height = 0;
  166. }
  167. GdkPaintable *
  168. m2_paintable_new (M2PaintableDrawFunc draw, int width, int height,
  169. gpointer user_data, GDestroyNotify destroy)
  170. {
  171. M2Paintable *self = g_object_new (m2_paintable_get_type (), NULL);
  172. self->draw = draw;
  173. self->width = width;
  174. self->height = height;
  175. self->user_data = user_data;
  176. self->destroy = destroy;
  177. return GDK_PAINTABLE (self);
  178. }
  179. /* ------------------------------------------------------------------ */
  180. /* M2ListModel (implements GListModel) */
  181. /* ------------------------------------------------------------------ */
  182. typedef guint (*M2ListModelGetNItemsFunc) (gpointer user_data);
  183. typedef gpointer (*M2ListModelGetItemFunc) (guint position,
  184. gpointer user_data);
  185. typedef struct _M2ListModel M2ListModel;
  186. typedef struct _M2ListModelClass M2ListModelClass;
  187. struct _M2ListModel
  188. {
  189. GObject parent_instance;
  190. M2ListModelGetNItemsFunc get_n_items;
  191. M2ListModelGetItemFunc get_item;
  192. gpointer user_data;
  193. GDestroyNotify destroy;
  194. GType item_type;
  195. };
  196. struct _M2ListModelClass
  197. {
  198. GObjectClass parent_class;
  199. };
  200. static guint
  201. m2_list_model_get_n_items (GListModel *list)
  202. {
  203. M2ListModel *self = (M2ListModel *) list;
  204. return self->get_n_items != NULL ? self->get_n_items (self->user_data) : 0;
  205. }
  206. static GType
  207. m2_list_model_get_item_type (GListModel *list)
  208. {
  209. return ((M2ListModel *) list)->item_type;
  210. }
  211. static gpointer
  212. m2_list_model_get_item (GListModel *list, guint position)
  213. {
  214. M2ListModel *self = (M2ListModel *) list;
  215. return self->get_item != NULL ? self->get_item (position, self->user_data)
  216. : NULL;
  217. }
  218. static void
  219. m2_list_model_list_init (GListModelInterface *iface)
  220. {
  221. iface->get_n_items = m2_list_model_get_n_items;
  222. iface->get_item_type = m2_list_model_get_item_type;
  223. iface->get_item = m2_list_model_get_item;
  224. }
  225. G_DEFINE_TYPE_WITH_CODE (M2ListModel, m2_list_model, G_TYPE_OBJECT,
  226. G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL,
  227. m2_list_model_list_init))
  228. static void
  229. m2_list_model_finalize (GObject *object)
  230. {
  231. M2ListModel *self = (M2ListModel *) object;
  232. if (self->destroy != NULL)
  233. self->destroy (self->user_data);
  234. G_OBJECT_CLASS (m2_list_model_parent_class)->finalize (object);
  235. }
  236. static void
  237. m2_list_model_class_init (M2ListModelClass *klass)
  238. {
  239. G_OBJECT_CLASS (klass)->finalize = m2_list_model_finalize;
  240. }
  241. static void
  242. m2_list_model_init (M2ListModel *self)
  243. {
  244. self->get_n_items = NULL;
  245. self->get_item = NULL;
  246. self->user_data = NULL;
  247. self->destroy = NULL;
  248. self->item_type = G_TYPE_OBJECT;
  249. }
  250. GListModel *
  251. m2_list_model_new (M2ListModelGetNItemsFunc get_n_items,
  252. M2ListModelGetItemFunc get_item, GType item_type,
  253. gpointer user_data, GDestroyNotify destroy)
  254. {
  255. M2ListModel *self = g_object_new (m2_list_model_get_type (), NULL);
  256. self->get_n_items = get_n_items;
  257. self->get_item = get_item;
  258. self->item_type = item_type;
  259. self->user_data = user_data;
  260. self->destroy = destroy;
  261. return G_LIST_MODEL (self);
  262. }
  263. void
  264. m2_list_model_items_changed (GListModel *model, guint position,
  265. guint removed, guint added)
  266. {
  267. g_list_model_items_changed (model, position, removed, added);
  268. }