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