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. */ |
19 | struct _GtkPuzzlePiece |
20 | { |
21 | GObject parent_instance; |
22 | |
23 | GdkPaintable *puzzle; |
24 | guint x; |
25 | guint y; |
26 | guint width; |
27 | guint height; |
28 | }; |
29 | |
30 | struct _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 | */ |
39 | static void |
40 | gtk_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 | |
63 | static GdkPaintableFlags |
64 | gtk_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 | |
74 | static int |
75 | gtk_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 | |
87 | static int |
88 | gtk_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 | |
97 | static double |
98 | gtk_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 | |
109 | static void |
110 | gtk_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 */ |
120 | G_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 | */ |
127 | static void |
128 | gtk_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 | */ |
144 | static void |
145 | gtk_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 | |
152 | static void |
153 | gtk_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 | */ |
161 | GdkPaintable * |
162 | gtk_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 | */ |
194 | GdkPaintable * |
195 | gtk_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 | |
207 | guint |
208 | gtk_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 | |
215 | guint |
216 | gtk_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 | |