1/* Paintable/A simple paintable
2 *
3 * GdkPaintable is an interface used by GTK for drawings of any sort
4 * that do not require layouting or positioning.
5 *
6 * This demo code gives a simple example on how a paintable can
7 * be created.
8 *
9 * Paintables can be used in many places inside GTK widgets, but the
10 * most common usage is inside GtkImage and that's what we're going
11 * to do here.
12 */
13
14#include <gtk/gtk.h>
15
16#include "puzzlepiece.h"
17
18/* Declare the struct. */
19struct _GtkPuzzlePiece
20{
21 GObject parent_instance;
22
23 GdkPaintable *puzzle;
24 guint x;
25 guint y;
26 guint width;
27 guint height;
28};
29
30struct _GtkPuzzlePieceClass
31{
32 GObjectClass parent_class;
33};
34
35/* This is the function that draws the puzzle piece.
36 * It just draws a rectangular cutout of the puzzle by clipping
37 * away the rest.
38 */
39static void
40gtk_puzzle_piece_snapshot (GdkPaintable *paintable,
41 GdkSnapshot *snapshot,
42 double width,
43 double height)
44{
45 GtkPuzzlePiece *self = GTK_PUZZLE_PIECE (ptr: paintable);
46
47 gtk_snapshot_push_clip (snapshot,
48 bounds: &GRAPHENE_RECT_INIT (0, 0, width, height));
49
50 gtk_snapshot_translate (snapshot,
51 point: &GRAPHENE_POINT_INIT (
52 - width * self->x,
53 - height * self->y
54 ));
55 gdk_paintable_snapshot (paintable: self->puzzle,
56 snapshot,
57 width: width * self->width,
58 height: height * self->height);
59
60 gtk_snapshot_pop (snapshot);
61}
62
63static GdkPaintableFlags
64gtk_puzzle_piece_get_flags (GdkPaintable *paintable)
65{
66 GtkPuzzlePiece *self = GTK_PUZZLE_PIECE (ptr: paintable);
67
68 /* The flags are the same as the ones of the puzzle.
69 * If the puzzle changes in some way, so do the pieces.
70 */
71 return gdk_paintable_get_flags (paintable: self->puzzle);
72}
73
74static int
75gtk_puzzle_piece_get_intrinsic_width (GdkPaintable *paintable)
76{
77 GtkPuzzlePiece *self = GTK_PUZZLE_PIECE (ptr: paintable);
78
79 /* We can compute our width relative to the puzzle.
80 * This logic even works for the case where the puzzle
81 * has no width, because the 0 return value is unchanged.
82 * Round up the value.
83 */
84 return (gdk_paintable_get_intrinsic_width (paintable: self->puzzle) + self->width - 1) / self->width;
85}
86
87static int
88gtk_puzzle_piece_get_intrinsic_height (GdkPaintable *paintable)
89{
90 GtkPuzzlePiece *self = GTK_PUZZLE_PIECE (ptr: paintable);
91
92 /* Do the same thing we did for the width with the height.
93 */
94 return (gdk_paintable_get_intrinsic_height (paintable: self->puzzle) + self->height - 1) / self->height;
95}
96
97static double
98gtk_puzzle_piece_get_intrinsic_aspect_ratio (GdkPaintable *paintable)
99{
100 GtkPuzzlePiece *self = GTK_PUZZLE_PIECE (ptr: paintable);
101
102 /* We can compute our aspect ratio relative to the puzzle.
103 * This logic again works for the case where the puzzle
104 * has no aspect ratio, because the 0 return value is unchanged.
105 */
106 return gdk_paintable_get_intrinsic_aspect_ratio (paintable: self->puzzle) * self->height / self->width;
107}
108
109static void
110gtk_puzzle_piece_paintable_init (GdkPaintableInterface *iface)
111{
112 iface->snapshot = gtk_puzzle_piece_snapshot;
113 iface->get_flags = gtk_puzzle_piece_get_flags;
114 iface->get_intrinsic_width = gtk_puzzle_piece_get_intrinsic_width;
115 iface->get_intrinsic_height = gtk_puzzle_piece_get_intrinsic_height;
116 iface->get_intrinsic_aspect_ratio = gtk_puzzle_piece_get_intrinsic_aspect_ratio;
117}
118
119/* When defining the GType, we need to implement the GdkPaintable interface */
120G_DEFINE_TYPE_WITH_CODE (GtkPuzzlePiece, gtk_puzzle_piece, G_TYPE_OBJECT,
121 G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
122 gtk_puzzle_piece_paintable_init))
123
124/* We need to declare a destructor to release our reference to the
125 * puzzle paintable and disconnect our signal handlers.
126 */
127static void
128gtk_puzzle_piece_dispose (GObject *object)
129{
130 GtkPuzzlePiece *self = GTK_PUZZLE_PIECE (ptr: object);
131
132 if (self->puzzle)
133 {
134 g_signal_handlers_disconnect_by_func (self->puzzle, gdk_paintable_invalidate_contents, self);
135 g_signal_handlers_disconnect_by_func (self->puzzle, gdk_paintable_invalidate_size, self);
136 g_clear_object (&self->puzzle);
137 }
138
139 G_OBJECT_CLASS (gtk_puzzle_piece_parent_class)->dispose (object);
140}
141
142/* Here's the boilerplate for the GObject declaration.
143 */
144static void
145gtk_puzzle_piece_class_init (GtkPuzzlePieceClass *klass)
146{
147 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
148
149 gobject_class->dispose = gtk_puzzle_piece_dispose;
150}
151
152static void
153gtk_puzzle_piece_init (GtkPuzzlePiece *self)
154{
155}
156
157/* And finally, we add a constructor.
158 * It is declared in the header so that the other examples
159 * can use it.
160 */
161GdkPaintable *
162gtk_puzzle_piece_new (GdkPaintable *puzzle,
163 guint x,
164 guint y,
165 guint width,
166 guint height)
167{
168 GtkPuzzlePiece *self;
169
170 /* These are sanity checks, so that we get warnings if we accidentally
171 * do anything stupid. */
172 g_return_val_if_fail (GDK_IS_PAINTABLE (puzzle), NULL);
173 g_return_val_if_fail (width > 0, NULL);
174 g_return_val_if_fail (height > 0, NULL);
175 g_return_val_if_fail (x < width, NULL);
176 g_return_val_if_fail (y < height, NULL);
177
178 self = g_object_new (GTK_TYPE_PUZZLE_PIECE, NULL);
179
180 self->puzzle = g_object_ref (puzzle);
181 g_signal_connect_swapped (puzzle, "invalidate-contents", G_CALLBACK (gdk_paintable_invalidate_contents), self);
182 g_signal_connect_swapped (puzzle, "invalidate-size", G_CALLBACK (gdk_paintable_invalidate_size), self);
183 self->x = x;
184 self->y = y;
185 self->width = width;
186 self->height = height;
187
188 return GDK_PAINTABLE (ptr: self);
189}
190
191/* Here are the accessors that we need to inspect the puzzle
192 * pieces in other code.
193 */
194GdkPaintable *
195gtk_puzzle_piece_get_puzzle (GtkPuzzlePiece *self)
196{
197 /* Add sanity checks here, too.
198 * If you make a habit out of this, you can always rely
199 * on your code having sanity checks, which makes it
200 * way easier to debug.
201 */
202 g_return_val_if_fail (GTK_IS_PUZZLE_PIECE (self), NULL);
203
204 return self->puzzle;
205}
206
207guint
208gtk_puzzle_piece_get_x (GtkPuzzlePiece *self)
209{
210 g_return_val_if_fail (GTK_IS_PUZZLE_PIECE (self), 0);
211
212 return self->x;
213}
214
215guint
216gtk_puzzle_piece_get_y (GtkPuzzlePiece *self)
217{
218 g_return_val_if_fail (GTK_IS_PUZZLE_PIECE (self), 0);
219
220 return self->y;
221}
222
223

source code of gtk/demos/gtk-demo/puzzlepiece.c