1#include <gtk/gtk.h>
2
3
4
5typedef struct _GtkFocusWidget GtkFocusWidget;
6typedef struct _GtkFocusWidgetClass GtkFocusWidgetClass;
7
8#define GTK_TYPE_FOCUS_WIDGET (gtk_focus_widget_get_type ())
9#define GTK_FOCUS_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, GTK_TYPE_FOCUS_WIDGET, GtkFocusWidget))
10#define GTK_FOCUS_WIDGET_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST(cls, GTK_TYPE_FOCUS_WIDGET, GtkFocusWidgetClass))
11#define GTK_IS_FOCUS_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, GTK_TYPE_FOCUS_WIDGET))
12#define GTK_IS_FOCUS_WIDGET_CLASS(cls) (G_TYPE_CHECK_CLASS_TYPE(cls, GTK_TYPE_FOCUS_WIDGET))
13#define GTK_FOCUS_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS(obj, GTK_TYPE_FOCUS_WIDGET, GtkFocusWidgetClass))
14
15const char *css =
16"* {"
17" transition: none; "
18"}"
19"focuswidget {"
20" padding: 30px;"
21" font-size: 70%;"
22"}"
23"focuswidget button:nth-child(1) {"
24" margin-right: 15px;"
25" margin-bottom: 15px;"
26"}"
27"focuswidget button:nth-child(2) {"
28" margin-left: 15px;"
29" margin-bottom: 15px;"
30"}"
31"focuswidget button:nth-child(3) {"
32" margin-right: 15px;"
33" margin-top: 15px;"
34"}"
35"focuswidget button:nth-child(4) {"
36" margin-left: 15px;"
37" margin-top: 15px;"
38"}"
39"focuswidget button {"
40" min-width: 80px;"
41" min-height: 80px;"
42" margin: 0px;"
43" border: 5px solid green;"
44" border-radius: 0px;"
45" padding: 10px;"
46" background-image: none;"
47" background-color: white;"
48" box-shadow: none;"
49"}"
50"focuswidget button:focus-visible {"
51" outline-width: 4px;"
52" outline-color: yellow;"
53"}"
54"focuswidget button:hover {"
55" background-color: black;"
56" color: white;"
57"}"
58"focuswidget button label:hover {"
59" background-color: green;"
60"}"
61;
62
63struct _GtkFocusWidget
64{
65 GtkWidget parent_instance;
66 double mouse_x;
67 double mouse_y;
68
69 union {
70 struct {
71 GtkWidget *child1;
72 GtkWidget *child2;
73 GtkWidget *child3;
74 GtkWidget *child4;
75 };
76 GtkWidget* children[4];
77 };
78};
79
80struct _GtkFocusWidgetClass
81{
82 GtkWidgetClass parent_class;
83};
84
85GType gtk_focus_widget_get_type (void) G_GNUC_CONST;
86
87
88G_DEFINE_TYPE(GtkFocusWidget, gtk_focus_widget, GTK_TYPE_WIDGET)
89
90static void
91gtk_focus_widget_size_allocate (GtkWidget *widget,
92 int width,
93 int height,
94 int baseline)
95{
96 GtkFocusWidget *self = GTK_FOCUS_WIDGET (widget);
97 int child_width = width / 2;
98 int child_height = height / 2;
99 GtkAllocation child_alloc;
100
101 child_alloc.x = 0;
102 child_alloc.y = 0;
103 child_alloc.width = child_width;
104 child_alloc.height = child_height;
105
106 gtk_widget_size_allocate (widget: self->child1, allocation: &child_alloc, baseline: -1);
107
108 child_alloc.x += child_width;
109
110 gtk_widget_size_allocate (widget: self->child2, allocation: &child_alloc, baseline: -1);
111
112 child_alloc.y += child_height;
113
114 gtk_widget_size_allocate (widget: self->child4, allocation: &child_alloc, baseline: -1);
115
116 child_alloc.x -= child_width;
117
118 gtk_widget_size_allocate (widget: self->child3, allocation: &child_alloc, baseline: -1);
119}
120
121static void
122gtk_focus_widget_measure (GtkWidget *widget,
123 GtkOrientation orientation,
124 int for_size,
125 int *minimum,
126 int *natural,
127 int *minimum_baseline,
128 int *natural_baseline)
129{
130 GtkFocusWidget *self = GTK_FOCUS_WIDGET (widget);
131 int min, nat;
132 int i;
133
134 *minimum = 0;
135 *natural = 0;
136
137 for (i = 0; i < 4; i ++)
138 {
139 gtk_widget_measure (widget: self->children[i], orientation, for_size,
140 minimum: &min, natural: &nat, NULL, NULL);
141
142 *minimum = MAX (*minimum, min);
143 *natural = MAX (*natural, nat);
144 }
145
146 *minimum *= 2;
147 *natural *= 2;
148}
149
150static void
151gtk_focus_widget_snapshot (GtkWidget *widget, GtkSnapshot *snapshot)
152{
153 GtkFocusWidget *self = GTK_FOCUS_WIDGET (widget);
154
155 gtk_widget_snapshot_child (widget, child: self->child1, snapshot);
156 gtk_widget_snapshot_child (widget, child: self->child2, snapshot);
157 gtk_widget_snapshot_child (widget, child: self->child3, snapshot);
158 gtk_widget_snapshot_child (widget, child: self->child4, snapshot);
159
160 if (self->mouse_x != G_MININT && self->mouse_y != G_MININT)
161 {
162 PangoLayout *layout;
163 char *text;
164 GtkAllocation alloc;
165 graphene_rect_t bounds;
166 GdkRGBA black = {0, 0, 0, 1};
167
168 gtk_widget_get_allocation (widget, allocation: &alloc);
169
170 /* Since event coordinates and drawing is supposed to happen in the
171 * same coordinates space, this should all work out just fine. */
172 bounds.origin.x = self->mouse_x;
173 bounds.origin.y = -30;
174 bounds.size.width = 1;
175 bounds.size.height = alloc.height;
176 gtk_snapshot_append_color (snapshot,
177 color: &black,
178 bounds: &bounds);
179
180 bounds.origin.x = -30;
181 bounds.origin.y = self->mouse_y;
182 bounds.size.width = alloc.width;
183 bounds.size.height = 1;
184 gtk_snapshot_append_color (snapshot,
185 color: &black,
186 bounds: &bounds);
187
188 layout = gtk_widget_create_pango_layout (widget, NULL);
189 text = g_strdup_printf (format: "%.2f×%.2f", self->mouse_x, self->mouse_y);
190 pango_layout_set_text (layout, text, length: -1);
191
192 gtk_snapshot_render_layout (snapshot,
193 context: gtk_widget_get_style_context (widget),
194 x: self->mouse_x + 2,
195 y: self->mouse_y - 15, /* *shrug* */
196 layout);
197
198 g_free (mem: text);
199 g_object_unref (object: layout);
200 }
201}
202
203static void
204motion_cb (GtkEventControllerMotion *controller,
205 double x,
206 double y,
207 GtkWidget *widget)
208{
209 GtkFocusWidget *self = GTK_FOCUS_WIDGET (widget);
210
211 self->mouse_x = x;
212 self->mouse_y = y;
213
214 gtk_widget_queue_draw (widget);
215}
216
217static void
218gtk_focus_widget_finalize (GObject *object)
219{
220 GtkFocusWidget *self = GTK_FOCUS_WIDGET (object);
221
222 gtk_widget_unparent (widget: self->child1);
223 gtk_widget_unparent (widget: self->child2);
224 gtk_widget_unparent (widget: self->child3);
225 gtk_widget_unparent (widget: self->child4);
226
227 G_OBJECT_CLASS (gtk_focus_widget_parent_class)->finalize (object);
228}
229
230static void
231gtk_focus_widget_init (GtkFocusWidget *self)
232{
233 GtkEventController *controller;
234
235 self->child1 = gtk_button_new_with_label (label: "1");
236 gtk_widget_set_parent (widget: self->child1, GTK_WIDGET (self));
237 self->child2 = gtk_button_new_with_label (label: "2");
238 gtk_widget_set_parent (widget: self->child2, GTK_WIDGET (self));
239 self->child3 = gtk_button_new_with_label (label: "3");
240 gtk_widget_set_parent (widget: self->child3, GTK_WIDGET (self));
241 self->child4 = gtk_button_new_with_label (label: "4");
242 gtk_widget_set_parent (widget: self->child4, GTK_WIDGET (self));
243
244 self->mouse_x = G_MININT;
245 self->mouse_y = G_MININT;
246
247 controller = gtk_event_controller_motion_new ();
248 g_signal_connect (controller, "motion",
249 G_CALLBACK (motion_cb), self);
250 gtk_widget_add_controller (GTK_WIDGET (self), controller);
251}
252
253static void
254gtk_focus_widget_class_init (GtkFocusWidgetClass *klass)
255{
256 GObjectClass *object_class = G_OBJECT_CLASS (klass);
257 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
258
259 object_class->finalize = gtk_focus_widget_finalize;
260
261 widget_class->snapshot = gtk_focus_widget_snapshot;
262 widget_class->measure = gtk_focus_widget_measure;
263 widget_class->size_allocate = gtk_focus_widget_size_allocate;
264
265 gtk_widget_class_set_css_name (widget_class, name: "focuswidget");
266}
267
268static void
269quit_cb (GtkWidget *widget,
270 gpointer user_data)
271{
272 gboolean *is_done = user_data;
273
274 *is_done = TRUE;
275
276 g_main_context_wakeup (NULL);
277}
278
279int
280main(int argc, char **argv)
281{
282 GtkWidget *window;
283 GtkWidget *widget;
284 GtkCssProvider *provider;
285 gboolean done = FALSE;
286
287 gtk_init ();
288
289 provider = gtk_css_provider_new ();
290 gtk_css_provider_load_from_data (css_provider: provider, data: css, length: -1);
291 gtk_style_context_add_provider_for_display (display: gdk_display_get_default (),
292 GTK_STYLE_PROVIDER (provider),
293 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
294
295 window = gtk_window_new ();
296 widget = g_object_new (GTK_TYPE_FOCUS_WIDGET, NULL);
297
298 gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
299
300 gtk_window_set_child (GTK_WINDOW (window), child: widget);
301 g_signal_connect (window, "destroy", G_CALLBACK (quit_cb), &done);
302
303 gtk_widget_show (widget: window);
304
305 while (!done)
306 g_main_context_iteration (NULL, TRUE);
307}
308

source code of gtk/tests/testwidgetfocus.c