1 | #include <gtk/gtk.h> |
2 | |
3 | |
4 | |
5 | typedef struct _GtkFocusWidget GtkFocusWidget; |
6 | typedef 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 | |
15 | const 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 | |
63 | struct _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 | |
80 | struct _GtkFocusWidgetClass |
81 | { |
82 | GtkWidgetClass parent_class; |
83 | }; |
84 | |
85 | GType gtk_focus_widget_get_type (void) G_GNUC_CONST; |
86 | |
87 | |
88 | G_DEFINE_TYPE(GtkFocusWidget, gtk_focus_widget, GTK_TYPE_WIDGET) |
89 | |
90 | static void |
91 | gtk_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 | |
121 | static void |
122 | gtk_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 | |
150 | static void |
151 | gtk_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 | |
203 | static void |
204 | motion_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 | |
217 | static void |
218 | gtk_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 | |
230 | static void |
231 | gtk_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 | |
253 | static void |
254 | gtk_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 | |
268 | static void |
269 | quit_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 | |
279 | int |
280 | main(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 | |