1 | /* GTK - The GIMP Toolkit |
2 | * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald |
3 | * |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Lesser General Public |
6 | * License as published by the Free Software Foundation; either |
7 | * version 2 of the License, or (at your option) any later version. |
8 | * |
9 | * This library is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | * Lesser General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU Lesser General Public |
15 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
16 | */ |
17 | |
18 | /* |
19 | * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS |
20 | * file for a list of people on the GTK+ Team. See the ChangeLog |
21 | * files for a list of changes. These files are distributed with |
22 | * GTK+ at ftp://ftp.gtk.org/pub/gtk/. |
23 | */ |
24 | |
25 | #include "config.h" |
26 | #include <string.h> |
27 | #include "gtkframe.h" |
28 | #include "gtklabel.h" |
29 | #include "gtkprivate.h" |
30 | #include "gtktypebuiltins.h" |
31 | #include "gtkintl.h" |
32 | #include "gtkbuildable.h" |
33 | #include "gtkwidgetprivate.h" |
34 | #include "gtklabel.h" |
35 | |
36 | /** |
37 | * GtkFrame: |
38 | * |
39 | * `GtkFrame` is a widget that surrounds its child with a decorative |
40 | * frame and an optional label. |
41 | * |
42 | * ![An example GtkFrame](frame.png) |
43 | * |
44 | * If present, the label is drawn inside the top edge of the frame. |
45 | * The horizontal position of the label can be controlled with |
46 | * [method@Gtk.Frame.set_label_align]. |
47 | * |
48 | * `GtkFrame` clips its child. You can use this to add rounded corners |
49 | * to widgets, but be aware that it also cuts off shadows. |
50 | * |
51 | * # GtkFrame as GtkBuildable |
52 | * |
53 | * The `GtkFrame` implementation of the `GtkBuildable` interface supports |
54 | * placing a child in the label position by specifying “label” as the |
55 | * “type” attribute of a <child> element. A normal content child can |
56 | * be specified without specifying a <child> type attribute. |
57 | * |
58 | * An example of a UI definition fragment with GtkFrame: |
59 | * ```xml |
60 | * <object class="GtkFrame"> |
61 | * <child type="label"> |
62 | * <object class="GtkLabel" id="frame_label"/> |
63 | * </child> |
64 | * <child> |
65 | * <object class="GtkEntry" id="frame_content"/> |
66 | * </child> |
67 | * </object> |
68 | * ``` |
69 | * |
70 | * # CSS nodes |
71 | * |
72 | * ``` |
73 | * frame |
74 | * ├── <label widget> |
75 | * ╰── <child> |
76 | * ``` |
77 | * |
78 | * `GtkFrame` has a main CSS node with name “frame”, which is used to draw the |
79 | * visible border. You can set the appearance of the border using CSS properties |
80 | * like “border-style” on this node. |
81 | */ |
82 | |
83 | typedef struct |
84 | { |
85 | /* Properties */ |
86 | GtkWidget *label_widget; |
87 | GtkWidget *child; |
88 | |
89 | guint has_frame : 1; |
90 | float label_xalign; |
91 | } GtkFramePrivate; |
92 | |
93 | enum { |
94 | PROP_0, |
95 | PROP_LABEL, |
96 | PROP_LABEL_XALIGN, |
97 | PROP_LABEL_WIDGET, |
98 | PROP_CHILD, |
99 | LAST_PROP |
100 | }; |
101 | |
102 | static GParamSpec *frame_props[LAST_PROP]; |
103 | |
104 | static void gtk_frame_dispose (GObject *object); |
105 | static void gtk_frame_set_property (GObject *object, |
106 | guint param_id, |
107 | const GValue *value, |
108 | GParamSpec *pspec); |
109 | static void gtk_frame_get_property (GObject *object, |
110 | guint param_id, |
111 | GValue *value, |
112 | GParamSpec *pspec); |
113 | static void gtk_frame_size_allocate (GtkWidget *widget, |
114 | int width, |
115 | int height, |
116 | int baseline); |
117 | static void gtk_frame_real_compute_child_allocation (GtkFrame *frame, |
118 | GtkAllocation *child_allocation); |
119 | |
120 | /* GtkBuildable */ |
121 | static void gtk_frame_buildable_init (GtkBuildableIface *iface); |
122 | static void gtk_frame_buildable_add_child (GtkBuildable *buildable, |
123 | GtkBuilder *builder, |
124 | GObject *child, |
125 | const char *type); |
126 | static void gtk_frame_measure (GtkWidget *widget, |
127 | GtkOrientation orientation, |
128 | int for_size, |
129 | int *minimum_size, |
130 | int *natural_size, |
131 | int *minimum_baseline, |
132 | int *natural_baseline); |
133 | static void gtk_frame_compute_expand (GtkWidget *widget, |
134 | gboolean *hexpand, |
135 | gboolean *vexpand); |
136 | static GtkSizeRequestMode gtk_frame_get_request_mode (GtkWidget *widget); |
137 | |
138 | G_DEFINE_TYPE_WITH_CODE (GtkFrame, gtk_frame, GTK_TYPE_WIDGET, |
139 | G_ADD_PRIVATE (GtkFrame) |
140 | G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, |
141 | gtk_frame_buildable_init)) |
142 | |
143 | static void |
144 | gtk_frame_class_init (GtkFrameClass *class) |
145 | { |
146 | GObjectClass *gobject_class; |
147 | GtkWidgetClass *widget_class; |
148 | |
149 | gobject_class = (GObjectClass*) class; |
150 | widget_class = GTK_WIDGET_CLASS (class); |
151 | |
152 | gobject_class->dispose = gtk_frame_dispose; |
153 | gobject_class->set_property = gtk_frame_set_property; |
154 | gobject_class->get_property = gtk_frame_get_property; |
155 | |
156 | widget_class->size_allocate = gtk_frame_size_allocate; |
157 | widget_class->measure = gtk_frame_measure; |
158 | widget_class->compute_expand = gtk_frame_compute_expand; |
159 | widget_class->get_request_mode = gtk_frame_get_request_mode; |
160 | |
161 | class->compute_child_allocation = gtk_frame_real_compute_child_allocation; |
162 | |
163 | /** |
164 | * GtkFrame:label: (attributes org.gtk.Property.get=gtk_frame_get_label org.gtk.Property.set=gtk_frame_set_label) |
165 | * |
166 | * Text of the frame's label. |
167 | */ |
168 | frame_props[PROP_LABEL] = |
169 | g_param_spec_string (name: "label" , |
170 | P_("Label" ), |
171 | P_("Text of the frame’s label" ), |
172 | NULL, |
173 | GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); |
174 | |
175 | /** |
176 | * GtkFrame:label-xalign: (attributes org.gtk.Property.get=gtk_frame_get_label_align org.gtk.Property.set=gtk_frame_set_label_align) |
177 | * |
178 | * The horizontal alignment of the label. |
179 | */ |
180 | frame_props[PROP_LABEL_XALIGN] = |
181 | g_param_spec_float (name: "label-xalign" , |
182 | P_("Label xalign" ), |
183 | P_("The horizontal alignment of the label" ), |
184 | minimum: 0.0, maximum: 1.0, |
185 | default_value: 0.0, |
186 | GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); |
187 | |
188 | /** |
189 | * GtkFrame:label-widget: (attributes org.gtk.Property.get=gtk_frame_get_label_widget org.gtk.Property.set=gtk_frame_set_label_widget) |
190 | * |
191 | * Widget to display in place of the usual frame label. |
192 | */ |
193 | frame_props[PROP_LABEL_WIDGET] = |
194 | g_param_spec_object (name: "label-widget" , |
195 | P_("Label widget" ), |
196 | P_("A widget to display in place of the usual frame label" ), |
197 | GTK_TYPE_WIDGET, |
198 | GTK_PARAM_READWRITE); |
199 | |
200 | /** |
201 | * GtkFrame:child: (attributes org.gtk.Property.get=gtk_frame_get_child org.gtk.Property.set=gtk_frame_set_child) |
202 | * |
203 | * The child widget. |
204 | */ |
205 | frame_props[PROP_CHILD] = |
206 | g_param_spec_object (name: "child" , |
207 | P_("Child" ), |
208 | P_("The child widget" ), |
209 | GTK_TYPE_WIDGET, |
210 | GTK_PARAM_READWRITE); |
211 | |
212 | g_object_class_install_properties (oclass: gobject_class, n_pspecs: LAST_PROP, pspecs: frame_props); |
213 | |
214 | gtk_widget_class_set_css_name (widget_class, I_("frame" )); |
215 | } |
216 | |
217 | static GtkBuildableIface *parent_buildable_iface; |
218 | |
219 | static void |
220 | gtk_frame_buildable_init (GtkBuildableIface *iface) |
221 | { |
222 | parent_buildable_iface = g_type_interface_peek_parent (g_iface: iface); |
223 | |
224 | iface->add_child = gtk_frame_buildable_add_child; |
225 | } |
226 | |
227 | static void |
228 | gtk_frame_buildable_add_child (GtkBuildable *buildable, |
229 | GtkBuilder *builder, |
230 | GObject *child, |
231 | const char *type) |
232 | { |
233 | if (type && strcmp (s1: type, s2: "label" ) == 0) |
234 | gtk_frame_set_label_widget (GTK_FRAME (buildable), GTK_WIDGET (child)); |
235 | else if (GTK_IS_WIDGET (child)) |
236 | gtk_frame_set_child (GTK_FRAME (buildable), GTK_WIDGET (child)); |
237 | else |
238 | parent_buildable_iface->add_child (buildable, builder, child, type); |
239 | } |
240 | |
241 | static void |
242 | gtk_frame_init (GtkFrame *frame) |
243 | { |
244 | GtkFramePrivate *priv = gtk_frame_get_instance_private (self: frame); |
245 | |
246 | gtk_widget_set_overflow (GTK_WIDGET (frame), overflow: GTK_OVERFLOW_HIDDEN); |
247 | |
248 | priv->label_widget = NULL; |
249 | priv->has_frame = TRUE; |
250 | priv->label_xalign = 0.0; |
251 | } |
252 | |
253 | static void |
254 | gtk_frame_dispose (GObject *object) |
255 | { |
256 | GtkFrame *frame = GTK_FRAME (object); |
257 | GtkFramePrivate *priv = gtk_frame_get_instance_private (self: frame); |
258 | |
259 | g_clear_pointer (&priv->label_widget, gtk_widget_unparent); |
260 | g_clear_pointer (&priv->child, gtk_widget_unparent); |
261 | |
262 | G_OBJECT_CLASS (gtk_frame_parent_class)->dispose (object); |
263 | } |
264 | |
265 | static void |
266 | gtk_frame_set_property (GObject *object, |
267 | guint prop_id, |
268 | const GValue *value, |
269 | GParamSpec *pspec) |
270 | { |
271 | GtkFrame *frame = GTK_FRAME (object); |
272 | |
273 | switch (prop_id) |
274 | { |
275 | case PROP_LABEL: |
276 | gtk_frame_set_label (frame, label: g_value_get_string (value)); |
277 | break; |
278 | case PROP_LABEL_XALIGN: |
279 | gtk_frame_set_label_align (frame, xalign: g_value_get_float (value)); |
280 | break; |
281 | case PROP_LABEL_WIDGET: |
282 | gtk_frame_set_label_widget (frame, label_widget: g_value_get_object (value)); |
283 | break; |
284 | case PROP_CHILD: |
285 | gtk_frame_set_child (frame, child: g_value_get_object (value)); |
286 | break; |
287 | default: |
288 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
289 | break; |
290 | } |
291 | } |
292 | |
293 | static void |
294 | gtk_frame_get_property (GObject *object, |
295 | guint prop_id, |
296 | GValue *value, |
297 | GParamSpec *pspec) |
298 | { |
299 | GtkFrame *frame = GTK_FRAME (object); |
300 | GtkFramePrivate *priv = gtk_frame_get_instance_private (self: frame); |
301 | |
302 | switch (prop_id) |
303 | { |
304 | case PROP_LABEL: |
305 | g_value_set_string (value, v_string: gtk_frame_get_label (frame)); |
306 | break; |
307 | case PROP_LABEL_XALIGN: |
308 | g_value_set_float (value, v_float: priv->label_xalign); |
309 | break; |
310 | case PROP_LABEL_WIDGET: |
311 | g_value_set_object (value, |
312 | v_object: priv->label_widget ? |
313 | G_OBJECT (priv->label_widget) : NULL); |
314 | break; |
315 | case PROP_CHILD: |
316 | g_value_set_object (value, v_object: gtk_frame_get_child (frame)); |
317 | break; |
318 | default: |
319 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
320 | break; |
321 | } |
322 | } |
323 | |
324 | /** |
325 | * gtk_frame_new: |
326 | * @label: (nullable): the text to use as the label of the frame |
327 | * |
328 | * Creates a new `GtkFrame`, with optional label @label. |
329 | * |
330 | * If @label is %NULL, the label is omitted. |
331 | * |
332 | * Returns: a new `GtkFrame` widget |
333 | */ |
334 | GtkWidget* |
335 | gtk_frame_new (const char *label) |
336 | { |
337 | return g_object_new (GTK_TYPE_FRAME, first_property_name: "label" , label, NULL); |
338 | } |
339 | |
340 | /** |
341 | * gtk_frame_set_label: (attributes org.gtk.Method.set_property=label) |
342 | * @frame: a `GtkFrame` |
343 | * @label: (nullable): the text to use as the label of the frame |
344 | * |
345 | * Creates a new `GtkLabel` with the @label and sets it as the frame's |
346 | * label widget. |
347 | */ |
348 | void |
349 | gtk_frame_set_label (GtkFrame *frame, |
350 | const char *label) |
351 | { |
352 | g_return_if_fail (GTK_IS_FRAME (frame)); |
353 | |
354 | if (!label) |
355 | gtk_frame_set_label_widget (frame, NULL); |
356 | else |
357 | gtk_frame_set_label_widget (frame, label_widget: gtk_label_new (str: label)); |
358 | } |
359 | |
360 | /** |
361 | * gtk_frame_get_label: (attributes org.gtk.Method.get_property=label) |
362 | * @frame: a `GtkFrame` |
363 | * |
364 | * Returns the frame labels text. |
365 | * |
366 | * If the frame's label widget is not a `GtkLabel`, %NULL |
367 | * is returned. |
368 | * |
369 | * Returns: (nullable): the text in the label, or %NULL if there |
370 | * was no label widget or the label widget was not a `GtkLabel`. |
371 | * This string is owned by GTK and must not be modified or freed. |
372 | */ |
373 | const char * |
374 | gtk_frame_get_label (GtkFrame *frame) |
375 | { |
376 | GtkFramePrivate *priv = gtk_frame_get_instance_private (self: frame); |
377 | |
378 | g_return_val_if_fail (GTK_IS_FRAME (frame), NULL); |
379 | |
380 | if (GTK_IS_LABEL (priv->label_widget)) |
381 | return gtk_label_get_text (GTK_LABEL (priv->label_widget)); |
382 | else |
383 | return NULL; |
384 | } |
385 | |
386 | static void |
387 | update_accessible_relation (GtkFrame *frame) |
388 | { |
389 | GtkFramePrivate *priv = gtk_frame_get_instance_private (self: frame); |
390 | |
391 | if (!priv->child) |
392 | return; |
393 | |
394 | if (priv->label_widget) |
395 | gtk_accessible_update_relation (self: GTK_ACCESSIBLE (ptr: priv->child), |
396 | first_relation: GTK_ACCESSIBLE_RELATION_LABELLED_BY, priv->label_widget, NULL, |
397 | -1); |
398 | else |
399 | gtk_accessible_reset_relation (self: GTK_ACCESSIBLE (ptr: priv->child), |
400 | relation: GTK_ACCESSIBLE_RELATION_LABELLED_BY); |
401 | } |
402 | |
403 | /** |
404 | * gtk_frame_set_label_widget: (attributes org.gtk.Method.get_property=label-widget) |
405 | * @frame: a `GtkFrame` |
406 | * @label_widget: (nullable): the new label widget |
407 | * |
408 | * Sets the label widget for the frame. |
409 | * |
410 | * This is the widget that will appear embedded in the top edge |
411 | * of the frame as a title. |
412 | */ |
413 | void |
414 | gtk_frame_set_label_widget (GtkFrame *frame, |
415 | GtkWidget *label_widget) |
416 | { |
417 | GtkFramePrivate *priv = gtk_frame_get_instance_private (self: frame); |
418 | |
419 | g_return_if_fail (GTK_IS_FRAME (frame)); |
420 | g_return_if_fail (label_widget == NULL || GTK_IS_WIDGET (label_widget)); |
421 | g_return_if_fail (label_widget == NULL || gtk_widget_get_parent (label_widget) == NULL); |
422 | |
423 | if (priv->label_widget == label_widget) |
424 | return; |
425 | |
426 | if (priv->label_widget) |
427 | gtk_widget_unparent (widget: priv->label_widget); |
428 | |
429 | priv->label_widget = label_widget; |
430 | |
431 | if (label_widget) |
432 | { |
433 | priv->label_widget = label_widget; |
434 | gtk_widget_set_parent (widget: label_widget, GTK_WIDGET (frame)); |
435 | } |
436 | |
437 | update_accessible_relation (frame); |
438 | |
439 | g_object_freeze_notify (G_OBJECT (frame)); |
440 | g_object_notify_by_pspec (G_OBJECT (frame), pspec: frame_props[PROP_LABEL_WIDGET]); |
441 | g_object_notify_by_pspec (G_OBJECT (frame), pspec: frame_props[PROP_LABEL]); |
442 | g_object_thaw_notify (G_OBJECT (frame)); |
443 | } |
444 | |
445 | /** |
446 | * gtk_frame_get_label_widget: (attributes org.gtk.Method.get_property=label-widget) |
447 | * @frame: a `GtkFrame` |
448 | * |
449 | * Retrieves the label widget for the frame. |
450 | * |
451 | * Returns: (nullable) (transfer none): the label widget |
452 | */ |
453 | GtkWidget * |
454 | gtk_frame_get_label_widget (GtkFrame *frame) |
455 | { |
456 | GtkFramePrivate *priv = gtk_frame_get_instance_private (self: frame); |
457 | |
458 | g_return_val_if_fail (GTK_IS_FRAME (frame), NULL); |
459 | |
460 | return priv->label_widget; |
461 | } |
462 | |
463 | /** |
464 | * gtk_frame_set_label_align: (attributes org.gtk.Method.set_property=label-xalign) |
465 | * @frame: a `GtkFrame` |
466 | * @xalign: The position of the label along the top edge |
467 | * of the widget. A value of 0.0 represents left alignment; |
468 | * 1.0 represents right alignment. |
469 | * |
470 | * Sets the X alignment of the frame widget’s label. |
471 | * |
472 | * The default value for a newly created frame is 0.0. |
473 | */ |
474 | void |
475 | gtk_frame_set_label_align (GtkFrame *frame, |
476 | float xalign) |
477 | { |
478 | GtkFramePrivate *priv = gtk_frame_get_instance_private (self: frame); |
479 | |
480 | g_return_if_fail (GTK_IS_FRAME (frame)); |
481 | |
482 | xalign = CLAMP (xalign, 0.0, 1.0); |
483 | if (priv->label_xalign == xalign) |
484 | return; |
485 | |
486 | priv->label_xalign = xalign; |
487 | g_object_notify_by_pspec (G_OBJECT (frame), pspec: frame_props[PROP_LABEL_XALIGN]); |
488 | gtk_widget_queue_allocate (GTK_WIDGET (frame)); |
489 | } |
490 | |
491 | /** |
492 | * gtk_frame_get_label_align: (attributes org.gtk.Method.get_property=label-xalign) |
493 | * @frame: a `GtkFrame` |
494 | * |
495 | * Retrieves the X alignment of the frame’s label. |
496 | * |
497 | * Returns: the frames X alignment |
498 | */ |
499 | float |
500 | gtk_frame_get_label_align (GtkFrame *frame) |
501 | { |
502 | GtkFramePrivate *priv = gtk_frame_get_instance_private (self: frame); |
503 | |
504 | g_return_val_if_fail (GTK_IS_FRAME (frame), 0.0); |
505 | |
506 | return priv->label_xalign; |
507 | } |
508 | |
509 | static void |
510 | gtk_frame_size_allocate (GtkWidget *widget, |
511 | int width, |
512 | int height, |
513 | int baseline) |
514 | { |
515 | GtkFrame *frame = GTK_FRAME (widget); |
516 | GtkFramePrivate *priv = gtk_frame_get_instance_private (self: frame); |
517 | GtkAllocation new_allocation; |
518 | |
519 | GTK_FRAME_GET_CLASS (frame)->compute_child_allocation (frame, &new_allocation); |
520 | |
521 | if (priv->label_widget && |
522 | gtk_widget_get_visible (widget: priv->label_widget)) |
523 | { |
524 | GtkAllocation label_allocation; |
525 | int nat_width, label_width, label_height; |
526 | float xalign; |
527 | |
528 | if (_gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) |
529 | xalign = priv->label_xalign; |
530 | else |
531 | xalign = 1 - priv->label_xalign; |
532 | |
533 | gtk_widget_measure (widget: priv->label_widget, orientation: GTK_ORIENTATION_HORIZONTAL, for_size: -1, |
534 | NULL, natural: &nat_width, NULL, NULL); |
535 | label_width = MIN (new_allocation.width, nat_width); |
536 | gtk_widget_measure (widget: priv->label_widget, orientation: GTK_ORIENTATION_VERTICAL, for_size: width, |
537 | minimum: &label_height, NULL, NULL, NULL); |
538 | |
539 | label_allocation.x = new_allocation.x + (new_allocation.width - label_width) * xalign; |
540 | label_allocation.y = new_allocation.y - label_height; |
541 | label_allocation.height = label_height; |
542 | label_allocation.width = label_width; |
543 | |
544 | gtk_widget_size_allocate (widget: priv->label_widget, allocation: &label_allocation, baseline: -1); |
545 | } |
546 | |
547 | if (priv->child && gtk_widget_get_visible (widget: priv->child)) |
548 | gtk_widget_size_allocate (widget: priv->child, allocation: &new_allocation, baseline: -1); |
549 | } |
550 | |
551 | static void |
552 | gtk_frame_real_compute_child_allocation (GtkFrame *frame, |
553 | GtkAllocation *child_allocation) |
554 | { |
555 | GtkFramePrivate *priv = gtk_frame_get_instance_private (self: frame); |
556 | int frame_width, frame_height; |
557 | int height; |
558 | |
559 | frame_width = gtk_widget_get_width (GTK_WIDGET (frame)); |
560 | frame_height = gtk_widget_get_height (GTK_WIDGET (frame)); |
561 | |
562 | if (priv->label_widget) |
563 | { |
564 | int nat_width, width; |
565 | |
566 | gtk_widget_measure (widget: priv->label_widget, orientation: GTK_ORIENTATION_HORIZONTAL, for_size: -1, |
567 | NULL, natural: &nat_width, NULL, NULL); |
568 | width = MIN (frame_width, nat_width); |
569 | gtk_widget_measure (widget: priv->label_widget, orientation: GTK_ORIENTATION_VERTICAL, for_size: width, |
570 | minimum: &height, NULL, NULL, NULL); |
571 | } |
572 | else |
573 | height = 0; |
574 | |
575 | child_allocation->x = 0; |
576 | child_allocation->y = height; |
577 | child_allocation->width = MAX (1, frame_width); |
578 | child_allocation->height = MAX (1, frame_height - height); |
579 | } |
580 | |
581 | static void |
582 | gtk_frame_measure (GtkWidget *widget, |
583 | GtkOrientation orientation, |
584 | int for_size, |
585 | int *minimum, |
586 | int *natural, |
587 | int *minimum_baseline, |
588 | int *natural_baseline) |
589 | { |
590 | GtkFrame *frame = GTK_FRAME (widget); |
591 | GtkFramePrivate *priv = gtk_frame_get_instance_private (self: frame); |
592 | int child_min, child_nat; |
593 | |
594 | if (priv->child && gtk_widget_get_visible (widget: priv->child)) |
595 | { |
596 | gtk_widget_measure (widget: priv->child, |
597 | orientation, for_size, |
598 | minimum: &child_min, natural: &child_nat, |
599 | NULL, NULL); |
600 | |
601 | *minimum = child_min; |
602 | *natural = child_nat; |
603 | } |
604 | else |
605 | { |
606 | *minimum = 0; |
607 | *natural = 0; |
608 | } |
609 | |
610 | if (priv->label_widget && gtk_widget_get_visible (widget: priv->label_widget)) |
611 | { |
612 | if (orientation == GTK_ORIENTATION_HORIZONTAL) |
613 | { |
614 | gtk_widget_measure (widget: priv->label_widget, |
615 | orientation, for_size: -1, |
616 | minimum: &child_min, natural: &child_nat, |
617 | NULL, NULL); |
618 | |
619 | *minimum = MAX (child_min, *minimum); |
620 | *natural = MAX (child_nat, *natural); |
621 | } |
622 | else |
623 | { |
624 | gtk_widget_measure (widget: priv->label_widget, |
625 | orientation, for_size, |
626 | minimum: &child_min, natural: &child_nat, |
627 | NULL, NULL); |
628 | |
629 | *minimum += child_min; |
630 | *natural += child_nat; |
631 | } |
632 | } |
633 | } |
634 | |
635 | static void |
636 | gtk_frame_compute_expand (GtkWidget *widget, |
637 | gboolean *hexpand, |
638 | gboolean *vexpand) |
639 | { |
640 | GtkFrame *frame = GTK_FRAME (widget); |
641 | GtkFramePrivate *priv = gtk_frame_get_instance_private (self: frame); |
642 | |
643 | if (priv->child) |
644 | { |
645 | *hexpand = gtk_widget_compute_expand (widget: priv->child, orientation: GTK_ORIENTATION_HORIZONTAL); |
646 | *vexpand = gtk_widget_compute_expand (widget: priv->child, orientation: GTK_ORIENTATION_VERTICAL); |
647 | } |
648 | else |
649 | { |
650 | *hexpand = FALSE; |
651 | *vexpand = FALSE; |
652 | } |
653 | } |
654 | |
655 | static GtkSizeRequestMode |
656 | gtk_frame_get_request_mode (GtkWidget *widget) |
657 | { |
658 | GtkFrame *frame = GTK_FRAME (widget); |
659 | GtkFramePrivate *priv = gtk_frame_get_instance_private (self: frame); |
660 | |
661 | if (priv->child) |
662 | return gtk_widget_get_request_mode (widget: priv->child); |
663 | else |
664 | return GTK_SIZE_REQUEST_CONSTANT_SIZE; |
665 | } |
666 | |
667 | /** |
668 | * gtk_frame_set_child: (attributes org.gtk.Method.set_property=child) |
669 | * @frame: a `GtkFrame` |
670 | * @child: (nullable): the child widget |
671 | * |
672 | * Sets the child widget of @frame. |
673 | */ |
674 | void |
675 | gtk_frame_set_child (GtkFrame *frame, |
676 | GtkWidget *child) |
677 | { |
678 | GtkFramePrivate *priv = gtk_frame_get_instance_private (self: frame); |
679 | |
680 | g_return_if_fail (GTK_IS_FRAME (frame)); |
681 | g_return_if_fail (child == NULL || GTK_IS_WIDGET (child)); |
682 | |
683 | if (priv->child == child) |
684 | return; |
685 | |
686 | g_clear_pointer (&priv->child, gtk_widget_unparent); |
687 | |
688 | if (child) |
689 | { |
690 | priv->child = child; |
691 | gtk_widget_set_parent (widget: child, GTK_WIDGET (frame)); |
692 | } |
693 | |
694 | update_accessible_relation (frame); |
695 | |
696 | g_object_notify_by_pspec (G_OBJECT (frame), pspec: frame_props[PROP_CHILD]); |
697 | } |
698 | |
699 | /** |
700 | * gtk_frame_get_child: (attributes org.gtk.Method.get_property=child) |
701 | * @frame: a `GtkFrame` |
702 | * |
703 | * Gets the child widget of @frame. |
704 | * |
705 | * Returns: (nullable) (transfer none): the child widget of @frame |
706 | */ |
707 | GtkWidget * |
708 | gtk_frame_get_child (GtkFrame *frame) |
709 | { |
710 | GtkFramePrivate *priv = gtk_frame_get_instance_private (self: frame); |
711 | |
712 | g_return_val_if_fail (GTK_IS_FRAME (frame), NULL); |
713 | |
714 | return priv->child; |
715 | } |
716 | |