1 | /* GTK - The GIMP Toolkit |
2 | * |
3 | * Copyright (C) 2003 Sun Microsystems, Inc. |
4 | * |
5 | * This library is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU Library General Public |
7 | * License as published by the Free Software Foundation; either |
8 | * version 2 of the License, or (at your option) any later version. |
9 | * |
10 | * This library is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | * Library General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU Library General Public |
16 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
17 | * |
18 | * Authors: |
19 | * Mark McLoughlin <mark@skynet.ie> |
20 | */ |
21 | |
22 | /** |
23 | * GtkExpander: |
24 | * |
25 | * `GtkExpander` allows the user to reveal its child by clicking |
26 | * on an expander triangle. |
27 | * |
28 | * ![An example GtkExpander](expander.png) |
29 | * |
30 | * This is similar to the triangles used in a `GtkTreeView`. |
31 | * |
32 | * Normally you use an expander as you would use a frame; you create |
33 | * the child widget and use [method@Gtk.Expander.set_child] to add it |
34 | * to the expander. When the expander is toggled, it will take care of |
35 | * showing and hiding the child automatically. |
36 | * |
37 | * # Special Usage |
38 | * |
39 | * There are situations in which you may prefer to show and hide the |
40 | * expanded widget yourself, such as when you want to actually create |
41 | * the widget at expansion time. In this case, create a `GtkExpander` |
42 | * but do not add a child to it. The expander widget has an |
43 | * [property@Gtk.Expander:expanded[ property which can be used to |
44 | * monitor its expansion state. You should watch this property with |
45 | * a signal connection as follows: |
46 | * |
47 | * ```c |
48 | * static void |
49 | * expander_callback (GObject *object, |
50 | * GParamSpec *param_spec, |
51 | * gpointer user_data) |
52 | * { |
53 | * GtkExpander *expander; |
54 | * |
55 | * expander = GTK_EXPANDER (object); |
56 | * |
57 | * if (gtk_expander_get_expanded (expander)) |
58 | * { |
59 | * // Show or create widgets |
60 | * } |
61 | * else |
62 | * { |
63 | * // Hide or destroy widgets |
64 | * } |
65 | * } |
66 | * |
67 | * static void |
68 | * create_expander (void) |
69 | * { |
70 | * GtkWidget *expander = gtk_expander_new_with_mnemonic ("_More Options"); |
71 | * g_signal_connect (expander, "notify::expanded", |
72 | * G_CALLBACK (expander_callback), NULL); |
73 | * |
74 | * // ... |
75 | * } |
76 | * ``` |
77 | * |
78 | * # GtkExpander as GtkBuildable |
79 | * |
80 | * The `GtkExpander` implementation of the `GtkBuildable` interface supports |
81 | * placing a child in the label position by specifying “label” as the |
82 | * “type” attribute of a <child> element. A normal content child can be |
83 | * specified without specifying a <child> type attribute. |
84 | * |
85 | * An example of a UI definition fragment with GtkExpander: |
86 | * |
87 | * ```xml |
88 | * <object class="GtkExpander"> |
89 | * <child type="label"> |
90 | * <object class="GtkLabel" id="expander-label"/> |
91 | * </child> |
92 | * <child> |
93 | * <object class="GtkEntry" id="expander-content"/> |
94 | * </child> |
95 | * </object> |
96 | * ``` |
97 | * |
98 | * # CSS nodes |
99 | * |
100 | * ``` |
101 | * expander |
102 | * ╰── box |
103 | * ├── title |
104 | * │ ├── arrow |
105 | * │ ╰── <label widget> |
106 | * ╰── <child> |
107 | * ``` |
108 | * |
109 | * `GtkExpander` has three CSS nodes, the main node with the name expander, |
110 | * a subnode with name title and node below it with name arrow. The arrow of an |
111 | * expander that is showing its child gets the :checked pseudoclass added to it. |
112 | * |
113 | * # Accessibility |
114 | * |
115 | * `GtkExpander` uses the %GTK_ACCESSIBLE_ROLE_BUTTON role. |
116 | */ |
117 | |
118 | #include "config.h" |
119 | |
120 | #include "gtkexpander.h" |
121 | |
122 | #include "gtkbox.h" |
123 | #include "gtkbuildable.h" |
124 | #include "gtkdropcontrollermotion.h" |
125 | #include "gtkbuiltiniconprivate.h" |
126 | #include "gtkgestureclick.h" |
127 | #include "gtkgesturesingle.h" |
128 | #include "gtkintl.h" |
129 | #include "gtklabel.h" |
130 | #include "gtkmarshalers.h" |
131 | #include "gtkmain.h" |
132 | #include "gtkprivate.h" |
133 | #include "gtkwidgetprivate.h" |
134 | |
135 | #include <string.h> |
136 | |
137 | #define TIMEOUT_EXPAND 500 |
138 | |
139 | enum |
140 | { |
141 | PROP_0, |
142 | PROP_EXPANDED, |
143 | PROP_LABEL, |
144 | PROP_USE_UNDERLINE, |
145 | PROP_USE_MARKUP, |
146 | PROP_LABEL_WIDGET, |
147 | PROP_RESIZE_TOPLEVEL, |
148 | PROP_CHILD |
149 | }; |
150 | |
151 | typedef struct _GtkExpanderClass GtkExpanderClass; |
152 | |
153 | struct _GtkExpander |
154 | { |
155 | GtkWidget parent_instance; |
156 | |
157 | GtkWidget *label_widget; |
158 | |
159 | GtkWidget *box; |
160 | GtkWidget *title_widget; |
161 | GtkWidget *arrow_widget; |
162 | GtkWidget *child; |
163 | |
164 | guint expand_timer; |
165 | |
166 | guint expanded : 1; |
167 | guint use_underline : 1; |
168 | guint use_markup : 1; |
169 | guint resize_toplevel : 1; |
170 | }; |
171 | |
172 | struct _GtkExpanderClass |
173 | { |
174 | GtkWidgetClass parent_class; |
175 | |
176 | void (* activate) (GtkExpander *expander); |
177 | }; |
178 | |
179 | static void gtk_expander_dispose (GObject *object); |
180 | static void gtk_expander_set_property (GObject *object, |
181 | guint prop_id, |
182 | const GValue *value, |
183 | GParamSpec *pspec); |
184 | static void gtk_expander_get_property (GObject *object, |
185 | guint prop_id, |
186 | GValue *value, |
187 | GParamSpec *pspec); |
188 | |
189 | static void gtk_expander_size_allocate (GtkWidget *widget, |
190 | int width, |
191 | int height, |
192 | int baseline); |
193 | static gboolean gtk_expander_focus (GtkWidget *widget, |
194 | GtkDirectionType direction); |
195 | |
196 | static void gtk_expander_activate (GtkExpander *expander); |
197 | |
198 | |
199 | /* GtkBuildable */ |
200 | static void gtk_expander_buildable_init (GtkBuildableIface *iface); |
201 | static void gtk_expander_buildable_add_child (GtkBuildable *buildable, |
202 | GtkBuilder *builder, |
203 | GObject *child, |
204 | const char *type); |
205 | |
206 | |
207 | /* GtkWidget */ |
208 | static void gtk_expander_measure (GtkWidget *widget, |
209 | GtkOrientation orientation, |
210 | int for_size, |
211 | int *minimum, |
212 | int *natural, |
213 | int *minimum_baseline, |
214 | int *natural_baseline); |
215 | |
216 | /* Gestures */ |
217 | static void gesture_click_released_cb (GtkGestureClick *gesture, |
218 | int n_press, |
219 | double x, |
220 | double y, |
221 | GtkExpander *expander); |
222 | |
223 | G_DEFINE_TYPE_WITH_CODE (GtkExpander, gtk_expander, GTK_TYPE_WIDGET, |
224 | G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, |
225 | gtk_expander_buildable_init)) |
226 | |
227 | static gboolean |
228 | expand_timeout (gpointer data) |
229 | { |
230 | GtkExpander *expander = GTK_EXPANDER (data); |
231 | |
232 | expander->expand_timer = 0; |
233 | gtk_expander_set_expanded (expander, TRUE); |
234 | |
235 | return FALSE; |
236 | } |
237 | |
238 | static void |
239 | gtk_expander_drag_enter (GtkDropControllerMotion *motion, |
240 | double x, |
241 | double y, |
242 | GtkExpander *expander) |
243 | { |
244 | if (!expander->expanded && !expander->expand_timer) |
245 | { |
246 | expander->expand_timer = g_timeout_add (TIMEOUT_EXPAND, function: (GSourceFunc) expand_timeout, data: expander); |
247 | gdk_source_set_static_name_by_id (tag: expander->expand_timer, name: "[gtk] expand_timeout" ); |
248 | } |
249 | } |
250 | |
251 | static void |
252 | gtk_expander_drag_leave (GtkDropControllerMotion *motion, |
253 | GtkExpander *expander) |
254 | { |
255 | if (expander->expand_timer) |
256 | { |
257 | g_source_remove (tag: expander->expand_timer); |
258 | expander->expand_timer = 0; |
259 | } |
260 | } |
261 | |
262 | static GtkSizeRequestMode |
263 | gtk_expander_get_request_mode (GtkWidget *widget) |
264 | { |
265 | GtkExpander *expander = GTK_EXPANDER (widget); |
266 | |
267 | if (expander->child) |
268 | return gtk_widget_get_request_mode (widget: expander->child); |
269 | else |
270 | return GTK_SIZE_REQUEST_CONSTANT_SIZE; |
271 | } |
272 | |
273 | static void |
274 | gtk_expander_compute_expand (GtkWidget *widget, |
275 | gboolean *hexpand, |
276 | gboolean *vexpand) |
277 | { |
278 | GtkExpander *expander = GTK_EXPANDER (widget); |
279 | |
280 | if (expander->child) |
281 | { |
282 | *hexpand = gtk_widget_compute_expand (widget: expander->child, orientation: GTK_ORIENTATION_HORIZONTAL); |
283 | *vexpand = gtk_widget_compute_expand (widget: expander->child, orientation: GTK_ORIENTATION_VERTICAL); |
284 | } |
285 | else |
286 | { |
287 | *hexpand = FALSE; |
288 | *vexpand = FALSE; |
289 | } |
290 | } |
291 | |
292 | static void |
293 | gtk_expander_class_init (GtkExpanderClass *klass) |
294 | { |
295 | GObjectClass *gobject_class = G_OBJECT_CLASS (klass); |
296 | GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); |
297 | guint activate_signal; |
298 | |
299 | gobject_class->dispose = gtk_expander_dispose; |
300 | gobject_class->set_property = gtk_expander_set_property; |
301 | gobject_class->get_property = gtk_expander_get_property; |
302 | |
303 | widget_class->size_allocate = gtk_expander_size_allocate; |
304 | widget_class->focus = gtk_expander_focus; |
305 | widget_class->grab_focus = gtk_widget_grab_focus_self; |
306 | widget_class->measure = gtk_expander_measure; |
307 | widget_class->compute_expand = gtk_expander_compute_expand; |
308 | widget_class->get_request_mode = gtk_expander_get_request_mode; |
309 | |
310 | klass->activate = gtk_expander_activate; |
311 | |
312 | /** |
313 | * GtkExpander:expanded: (attributes org.gtk.Property.get=gtk_expander_get_expanded org.gtk.Property.set=gtk_expander_set_expanded) |
314 | * |
315 | * Whether the expander has been opened to reveal the child. |
316 | */ |
317 | g_object_class_install_property (oclass: gobject_class, |
318 | property_id: PROP_EXPANDED, |
319 | pspec: g_param_spec_boolean (name: "expanded" , |
320 | P_("Expanded" ), |
321 | P_("Whether the expander has been opened to reveal the child widget" ), |
322 | FALSE, |
323 | GTK_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY)); |
324 | |
325 | /** |
326 | * GtkExpander:label: (attributes org.gtk.Property.get=gtk_expander_get_label org.gtk.Property.set=gtk_expander_set_label) |
327 | * |
328 | * The text of the expanders label. |
329 | */ |
330 | g_object_class_install_property (oclass: gobject_class, |
331 | property_id: PROP_LABEL, |
332 | pspec: g_param_spec_string (name: "label" , |
333 | P_("Label" ), |
334 | P_("Text of the expander’s label" ), |
335 | NULL, |
336 | GTK_PARAM_READWRITE|G_PARAM_CONSTRUCT)); |
337 | |
338 | /** |
339 | * GtkExpander:use-underline: (attributes org.gtk.Property.get=gtk_expander_get_use_underline org.gtk.Property.set=gtk_expander_set_use_underline) |
340 | * |
341 | * Whether an underline in the text indicates a mnemonic. |
342 | */ |
343 | g_object_class_install_property (oclass: gobject_class, |
344 | property_id: PROP_USE_UNDERLINE, |
345 | pspec: g_param_spec_boolean (name: "use-underline" , |
346 | P_("Use underline" ), |
347 | P_("If set, an underline in the text indicates the next character should be used for the mnemonic accelerator key" ), |
348 | FALSE, |
349 | GTK_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY)); |
350 | |
351 | /** |
352 | * GtkExpander:use-markup: (attributes org.gtk.Property.get=gtk_expander_get_use_markup org.gtk.Property.set=gtk_expander_set_use_markup) |
353 | * |
354 | * Whether the text in the label is Pango markup. |
355 | */ |
356 | g_object_class_install_property (oclass: gobject_class, |
357 | property_id: PROP_USE_MARKUP, |
358 | pspec: g_param_spec_boolean (name: "use-markup" , |
359 | P_("Use markup" ), |
360 | P_("The text of the label includes XML markup. See pango_parse_markup()" ), |
361 | FALSE, |
362 | GTK_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY)); |
363 | |
364 | /** |
365 | * GtkExpander:label-widget: (attributes org.gtk.Property.get=gtk_expander_get_label_widget org.gtk.Property.set=gtk_expander_set_label_widget) |
366 | * |
367 | * A widget to display instead of the usual expander label. |
368 | */ |
369 | g_object_class_install_property (oclass: gobject_class, |
370 | property_id: PROP_LABEL_WIDGET, |
371 | pspec: g_param_spec_object (name: "label-widget" , |
372 | P_("Label widget" ), |
373 | P_("A widget to display in place of the usual expander label" ), |
374 | GTK_TYPE_WIDGET, |
375 | GTK_PARAM_READWRITE)); |
376 | |
377 | /** |
378 | * GtkExpander:resize-toplevel: (attributes org.gtk.Property.get=gtk_expander_get_resize_toplevel org.gtk.Property.set=gtk_expander_set_resize_toplevel) |
379 | * |
380 | * When this property is %TRUE, the expander will resize the toplevel |
381 | * widget containing the expander upon expanding and collapsing. |
382 | */ |
383 | g_object_class_install_property (oclass: gobject_class, |
384 | property_id: PROP_RESIZE_TOPLEVEL, |
385 | pspec: g_param_spec_boolean (name: "resize-toplevel" , |
386 | P_("Resize toplevel" ), |
387 | P_("Whether the expander will resize the toplevel window upon expanding and collapsing" ), |
388 | FALSE, |
389 | GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); |
390 | |
391 | /** |
392 | * GtkExpander:child: (attributes org.gtk.Property.get=gtk_expander_get_child org.gtk.Property.set=gtk_expander_set_child) |
393 | * |
394 | * The child widget. |
395 | */ |
396 | g_object_class_install_property (oclass: gobject_class, |
397 | property_id: PROP_CHILD, |
398 | pspec: g_param_spec_object (name: "child" , |
399 | P_("Child" ), |
400 | P_("The child widget" ), |
401 | GTK_TYPE_WIDGET, |
402 | GTK_PARAM_READWRITE)); |
403 | |
404 | /** |
405 | * GtkExpander::activate: |
406 | * @expander: the `GtkExpander` that emitted the signal |
407 | * |
408 | * Activates the `GtkExpander`. |
409 | */ |
410 | activate_signal = |
411 | g_signal_new (I_("activate" ), |
412 | G_TYPE_FROM_CLASS (gobject_class), |
413 | signal_flags: G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, |
414 | G_STRUCT_OFFSET (GtkExpanderClass, activate), |
415 | NULL, NULL, |
416 | NULL, |
417 | G_TYPE_NONE, n_params: 0); |
418 | |
419 | gtk_widget_class_set_activate_signal (widget_class, signal_id: activate_signal); |
420 | gtk_widget_class_set_css_name (widget_class, I_("expander-widget" )); |
421 | gtk_widget_class_set_accessible_role (widget_class, accessible_role: GTK_ACCESSIBLE_ROLE_BUTTON); |
422 | } |
423 | |
424 | static void |
425 | gtk_expander_init (GtkExpander *expander) |
426 | { |
427 | GtkGesture *gesture; |
428 | GtkEventController *controller; |
429 | |
430 | expander->label_widget = NULL; |
431 | expander->child = NULL; |
432 | |
433 | expander->expanded = FALSE; |
434 | expander->use_underline = FALSE; |
435 | expander->use_markup = FALSE; |
436 | expander->expand_timer = 0; |
437 | expander->resize_toplevel = 0; |
438 | |
439 | gtk_widget_set_focusable (GTK_WIDGET (expander), TRUE); |
440 | |
441 | expander->box = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 0); |
442 | gtk_widget_set_parent (widget: expander->box, GTK_WIDGET (expander)); |
443 | |
444 | expander->title_widget = g_object_new (GTK_TYPE_BOX, |
445 | first_property_name: "css-name" , "title" , |
446 | NULL); |
447 | gtk_box_append (GTK_BOX (expander->box), child: expander->title_widget); |
448 | |
449 | expander->arrow_widget = gtk_builtin_icon_new (css_name: "expander" ); |
450 | gtk_widget_add_css_class (widget: expander->arrow_widget, css_class: "horizontal" ); |
451 | gtk_box_append (GTK_BOX (expander->title_widget), child: expander->arrow_widget); |
452 | |
453 | controller = gtk_drop_controller_motion_new (); |
454 | g_signal_connect (controller, "enter" , G_CALLBACK (gtk_expander_drag_enter), expander); |
455 | g_signal_connect (controller, "leave" , G_CALLBACK (gtk_expander_drag_leave), expander); |
456 | gtk_widget_add_controller (GTK_WIDGET (expander), controller); |
457 | |
458 | gesture = gtk_gesture_click_new (); |
459 | gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), |
460 | GDK_BUTTON_PRIMARY); |
461 | gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (gesture), |
462 | FALSE); |
463 | g_signal_connect (gesture, "released" , |
464 | G_CALLBACK (gesture_click_released_cb), expander); |
465 | gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (gesture), |
466 | phase: GTK_PHASE_BUBBLE); |
467 | gtk_widget_add_controller (GTK_WIDGET (expander->title_widget), GTK_EVENT_CONTROLLER (gesture)); |
468 | |
469 | gtk_accessible_update_state (self: GTK_ACCESSIBLE (ptr: expander), |
470 | first_state: GTK_ACCESSIBLE_STATE_EXPANDED, FALSE, |
471 | -1); |
472 | } |
473 | |
474 | static GtkBuildableIface *parent_buildable_iface; |
475 | |
476 | static void |
477 | gtk_expander_buildable_add_child (GtkBuildable *buildable, |
478 | GtkBuilder *builder, |
479 | GObject *child, |
480 | const char *type) |
481 | { |
482 | if (g_strcmp0 (str1: type, str2: "label" ) == 0) |
483 | gtk_expander_set_label_widget (GTK_EXPANDER (buildable), GTK_WIDGET (child)); |
484 | else if (GTK_IS_WIDGET (child)) |
485 | gtk_expander_set_child (GTK_EXPANDER (buildable), GTK_WIDGET (child)); |
486 | else |
487 | parent_buildable_iface->add_child (buildable, builder, child, type); |
488 | } |
489 | |
490 | static void |
491 | gtk_expander_buildable_init (GtkBuildableIface *iface) |
492 | { |
493 | parent_buildable_iface = g_type_interface_peek_parent (g_iface: iface); |
494 | |
495 | iface->add_child = gtk_expander_buildable_add_child; |
496 | } |
497 | |
498 | static void |
499 | gtk_expander_set_property (GObject *object, |
500 | guint prop_id, |
501 | const GValue *value, |
502 | GParamSpec *pspec) |
503 | { |
504 | GtkExpander *expander = GTK_EXPANDER (object); |
505 | |
506 | switch (prop_id) |
507 | { |
508 | case PROP_EXPANDED: |
509 | gtk_expander_set_expanded (expander, expanded: g_value_get_boolean (value)); |
510 | break; |
511 | case PROP_LABEL: |
512 | gtk_expander_set_label (expander, label: g_value_get_string (value)); |
513 | break; |
514 | case PROP_USE_UNDERLINE: |
515 | gtk_expander_set_use_underline (expander, use_underline: g_value_get_boolean (value)); |
516 | break; |
517 | case PROP_USE_MARKUP: |
518 | gtk_expander_set_use_markup (expander, use_markup: g_value_get_boolean (value)); |
519 | break; |
520 | case PROP_LABEL_WIDGET: |
521 | gtk_expander_set_label_widget (expander, label_widget: g_value_get_object (value)); |
522 | break; |
523 | case PROP_RESIZE_TOPLEVEL: |
524 | gtk_expander_set_resize_toplevel (expander, resize_toplevel: g_value_get_boolean (value)); |
525 | break; |
526 | case PROP_CHILD: |
527 | gtk_expander_set_child (expander, child: g_value_get_object (value)); |
528 | break; |
529 | default: |
530 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
531 | break; |
532 | } |
533 | } |
534 | |
535 | static void |
536 | gtk_expander_get_property (GObject *object, |
537 | guint prop_id, |
538 | GValue *value, |
539 | GParamSpec *pspec) |
540 | { |
541 | GtkExpander *expander = GTK_EXPANDER (object); |
542 | |
543 | switch (prop_id) |
544 | { |
545 | case PROP_EXPANDED: |
546 | g_value_set_boolean (value, v_boolean: expander->expanded); |
547 | break; |
548 | case PROP_LABEL: |
549 | g_value_set_string (value, v_string: gtk_expander_get_label (expander)); |
550 | break; |
551 | case PROP_USE_UNDERLINE: |
552 | g_value_set_boolean (value, v_boolean: expander->use_underline); |
553 | break; |
554 | case PROP_USE_MARKUP: |
555 | g_value_set_boolean (value, v_boolean: expander->use_markup); |
556 | break; |
557 | case PROP_LABEL_WIDGET: |
558 | g_value_set_object (value, |
559 | v_object: expander->label_widget ? |
560 | G_OBJECT (expander->label_widget) : NULL); |
561 | break; |
562 | case PROP_RESIZE_TOPLEVEL: |
563 | g_value_set_boolean (value, v_boolean: gtk_expander_get_resize_toplevel (expander)); |
564 | break; |
565 | case PROP_CHILD: |
566 | g_value_set_object (value, v_object: gtk_expander_get_child (expander)); |
567 | break; |
568 | default: |
569 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
570 | break; |
571 | } |
572 | } |
573 | |
574 | static void |
575 | gtk_expander_dispose (GObject *object) |
576 | { |
577 | GtkExpander *expander = GTK_EXPANDER (object); |
578 | |
579 | if (expander->expand_timer) |
580 | { |
581 | g_source_remove (tag: expander->expand_timer); |
582 | expander->expand_timer = 0; |
583 | } |
584 | |
585 | /* If the expander is not expanded, we own the child */ |
586 | if (!expander->expanded) |
587 | g_clear_object (&expander->child); |
588 | |
589 | if (expander->box) |
590 | { |
591 | gtk_widget_unparent (widget: expander->box); |
592 | expander->box = NULL; |
593 | expander->child = NULL; |
594 | expander->label_widget = NULL; |
595 | expander->arrow_widget = NULL; |
596 | } |
597 | |
598 | G_OBJECT_CLASS (gtk_expander_parent_class)->dispose (object); |
599 | } |
600 | |
601 | static void |
602 | gtk_expander_size_allocate (GtkWidget *widget, |
603 | int width, |
604 | int height, |
605 | int baseline) |
606 | { |
607 | GtkExpander *expander = GTK_EXPANDER (widget); |
608 | |
609 | gtk_widget_size_allocate (widget: expander->box, |
610 | allocation: &(GtkAllocation) { |
611 | 0, 0, |
612 | width, height |
613 | }, baseline); |
614 | } |
615 | |
616 | static void |
617 | gesture_click_released_cb (GtkGestureClick *gesture, |
618 | int n_press, |
619 | double x, |
620 | double y, |
621 | GtkExpander *expander) |
622 | { |
623 | gtk_widget_activate (GTK_WIDGET (expander)); |
624 | } |
625 | |
626 | typedef enum |
627 | { |
628 | FOCUS_NONE, |
629 | FOCUS_WIDGET, |
630 | FOCUS_LABEL, |
631 | FOCUS_CHILD |
632 | } FocusSite; |
633 | |
634 | static gboolean |
635 | focus_current_site (GtkExpander *expander, |
636 | GtkDirectionType direction) |
637 | { |
638 | GtkWidget *current_focus; |
639 | |
640 | current_focus = gtk_widget_get_focus_child (GTK_WIDGET (expander)); |
641 | |
642 | if (!current_focus) |
643 | return FALSE; |
644 | |
645 | return gtk_widget_child_focus (widget: current_focus, direction); |
646 | } |
647 | |
648 | static gboolean |
649 | focus_in_site (GtkExpander *expander, |
650 | FocusSite site, |
651 | GtkDirectionType direction) |
652 | { |
653 | switch (site) |
654 | { |
655 | case FOCUS_WIDGET: |
656 | gtk_widget_grab_focus (GTK_WIDGET (expander)); |
657 | return TRUE; |
658 | case FOCUS_LABEL: |
659 | if (expander->label_widget) |
660 | return gtk_widget_child_focus (widget: expander->label_widget, direction); |
661 | else |
662 | return FALSE; |
663 | case FOCUS_CHILD: |
664 | { |
665 | GtkWidget *child = expander->child; |
666 | |
667 | if (child && gtk_widget_get_child_visible (widget: child)) |
668 | return gtk_widget_child_focus (widget: child, direction); |
669 | else |
670 | return FALSE; |
671 | } |
672 | case FOCUS_NONE: |
673 | default: |
674 | break; |
675 | } |
676 | |
677 | g_assert_not_reached (); |
678 | return FALSE; |
679 | } |
680 | |
681 | static FocusSite |
682 | get_next_site (GtkExpander *expander, |
683 | FocusSite site, |
684 | GtkDirectionType direction) |
685 | { |
686 | gboolean ltr; |
687 | |
688 | ltr = gtk_widget_get_direction (GTK_WIDGET (expander)) != GTK_TEXT_DIR_RTL; |
689 | |
690 | switch (site) |
691 | { |
692 | case FOCUS_NONE: |
693 | switch (direction) |
694 | { |
695 | case GTK_DIR_TAB_BACKWARD: |
696 | case GTK_DIR_LEFT: |
697 | case GTK_DIR_UP: |
698 | return FOCUS_CHILD; |
699 | case GTK_DIR_TAB_FORWARD: |
700 | case GTK_DIR_DOWN: |
701 | case GTK_DIR_RIGHT: |
702 | default: |
703 | return FOCUS_WIDGET; |
704 | } |
705 | break; |
706 | case FOCUS_WIDGET: |
707 | switch (direction) |
708 | { |
709 | case GTK_DIR_TAB_BACKWARD: |
710 | case GTK_DIR_UP: |
711 | return FOCUS_NONE; |
712 | case GTK_DIR_LEFT: |
713 | return ltr ? FOCUS_NONE : FOCUS_LABEL; |
714 | case GTK_DIR_TAB_FORWARD: |
715 | case GTK_DIR_DOWN: |
716 | default: |
717 | return FOCUS_LABEL; |
718 | case GTK_DIR_RIGHT: |
719 | return ltr ? FOCUS_LABEL : FOCUS_NONE; |
720 | } |
721 | break; |
722 | case FOCUS_LABEL: |
723 | switch (direction) |
724 | { |
725 | case GTK_DIR_TAB_BACKWARD: |
726 | case GTK_DIR_UP: |
727 | return FOCUS_WIDGET; |
728 | case GTK_DIR_LEFT: |
729 | return ltr ? FOCUS_WIDGET : FOCUS_CHILD; |
730 | case GTK_DIR_TAB_FORWARD: |
731 | case GTK_DIR_DOWN: |
732 | default: |
733 | return FOCUS_CHILD; |
734 | case GTK_DIR_RIGHT: |
735 | return ltr ? FOCUS_CHILD : FOCUS_WIDGET; |
736 | } |
737 | break; |
738 | case FOCUS_CHILD: |
739 | switch (direction) |
740 | { |
741 | case GTK_DIR_TAB_BACKWARD: |
742 | case GTK_DIR_LEFT: |
743 | case GTK_DIR_UP: |
744 | return FOCUS_LABEL; |
745 | case GTK_DIR_TAB_FORWARD: |
746 | case GTK_DIR_DOWN: |
747 | case GTK_DIR_RIGHT: |
748 | default: |
749 | return FOCUS_NONE; |
750 | } |
751 | break; |
752 | default: |
753 | g_assert_not_reached (); |
754 | break; |
755 | } |
756 | |
757 | return FOCUS_NONE; |
758 | } |
759 | |
760 | static void |
761 | gtk_expander_resize_toplevel (GtkExpander *expander) |
762 | { |
763 | GtkWidget *child = expander->child; |
764 | |
765 | if (child && expander->resize_toplevel && |
766 | gtk_widget_get_realized (GTK_WIDGET (expander))) |
767 | { |
768 | GtkWidget *toplevel = GTK_WIDGET (gtk_widget_get_root (GTK_WIDGET (expander))); |
769 | |
770 | if (GTK_IS_WINDOW (toplevel) && |
771 | gtk_widget_get_realized (widget: toplevel)) |
772 | gtk_widget_queue_resize (GTK_WIDGET (expander)); |
773 | } |
774 | } |
775 | |
776 | static gboolean |
777 | gtk_expander_focus (GtkWidget *widget, |
778 | GtkDirectionType direction) |
779 | { |
780 | GtkExpander *expander = GTK_EXPANDER (widget); |
781 | |
782 | if (!focus_current_site (expander, direction)) |
783 | { |
784 | GtkWidget *old_focus_child; |
785 | gboolean widget_is_focus; |
786 | FocusSite site = FOCUS_NONE; |
787 | |
788 | widget_is_focus = gtk_widget_is_focus (widget); |
789 | old_focus_child = gtk_widget_get_focus_child (GTK_WIDGET (widget)); |
790 | |
791 | if (old_focus_child && old_focus_child == expander->label_widget) |
792 | site = FOCUS_LABEL; |
793 | else if (old_focus_child) |
794 | site = FOCUS_CHILD; |
795 | else if (widget_is_focus) |
796 | site = FOCUS_WIDGET; |
797 | |
798 | while ((site = get_next_site (expander, site, direction)) != FOCUS_NONE) |
799 | { |
800 | if (focus_in_site (expander, site, direction)) |
801 | return TRUE; |
802 | } |
803 | |
804 | return FALSE; |
805 | } |
806 | |
807 | return TRUE; |
808 | } |
809 | |
810 | static void |
811 | gtk_expander_activate (GtkExpander *expander) |
812 | { |
813 | gtk_expander_set_expanded (expander, expanded: !expander->expanded); |
814 | } |
815 | |
816 | static void |
817 | gtk_expander_measure (GtkWidget *widget, |
818 | GtkOrientation orientation, |
819 | int for_size, |
820 | int *minimum, |
821 | int *natural, |
822 | int *minimum_baseline, |
823 | int *natural_baseline) |
824 | { |
825 | GtkExpander *expander = GTK_EXPANDER (widget); |
826 | |
827 | gtk_widget_measure (widget: expander->box, |
828 | orientation, |
829 | for_size, |
830 | minimum, natural, |
831 | minimum_baseline, natural_baseline); |
832 | } |
833 | |
834 | /** |
835 | * gtk_expander_new: |
836 | * @label: (nullable): the text of the label |
837 | * |
838 | * Creates a new expander using @label as the text of the label. |
839 | * |
840 | * Returns: a new `GtkExpander` widget. |
841 | */ |
842 | GtkWidget * |
843 | gtk_expander_new (const char *label) |
844 | { |
845 | return g_object_new (GTK_TYPE_EXPANDER, first_property_name: "label" , label, NULL); |
846 | } |
847 | |
848 | /** |
849 | * gtk_expander_new_with_mnemonic: |
850 | * @label: (nullable): the text of the label with an underscore |
851 | * in front of the mnemonic character |
852 | * |
853 | * Creates a new expander using @label as the text of the label. |
854 | * |
855 | * If characters in @label are preceded by an underscore, they are |
856 | * underlined. If you need a literal underscore character in a label, |
857 | * use “__” (two underscores). The first underlined character represents |
858 | * a keyboard accelerator called a mnemonic. |
859 | * |
860 | * Pressing Alt and that key activates the button. |
861 | * |
862 | * Returns: a new `GtkExpander` widget. |
863 | */ |
864 | GtkWidget * |
865 | gtk_expander_new_with_mnemonic (const char *label) |
866 | { |
867 | return g_object_new (GTK_TYPE_EXPANDER, |
868 | first_property_name: "label" , label, |
869 | "use-underline" , TRUE, |
870 | NULL); |
871 | } |
872 | |
873 | /** |
874 | * gtk_expander_set_expanded: (attributes org.gtk.Method.set_property=expanded) |
875 | * @expander: a `GtkExpander` |
876 | * @expanded: whether the child widget is revealed |
877 | * |
878 | * Sets the state of the expander. |
879 | * |
880 | * Set to %TRUE, if you want the child widget to be revealed, |
881 | * and %FALSE if you want the child widget to be hidden. |
882 | */ |
883 | void |
884 | gtk_expander_set_expanded (GtkExpander *expander, |
885 | gboolean expanded) |
886 | { |
887 | GtkWidget *child; |
888 | |
889 | g_return_if_fail (GTK_IS_EXPANDER (expander)); |
890 | |
891 | expanded = expanded != FALSE; |
892 | |
893 | if (expander->expanded == expanded) |
894 | return; |
895 | |
896 | expander->expanded = expanded; |
897 | |
898 | if (expander->expanded) |
899 | gtk_widget_set_state_flags (widget: expander->arrow_widget, flags: GTK_STATE_FLAG_CHECKED, FALSE); |
900 | else |
901 | gtk_widget_unset_state_flags (widget: expander->arrow_widget, flags: GTK_STATE_FLAG_CHECKED); |
902 | |
903 | child = expander->child; |
904 | |
905 | if (child) |
906 | { |
907 | /* Transfer the ownership of the child to the box when |
908 | * expanded is set, and then back to us when it is unset |
909 | */ |
910 | if (expander->expanded) |
911 | { |
912 | gtk_box_append (GTK_BOX (expander->box), child); |
913 | g_object_unref (object: expander->child); |
914 | } |
915 | else |
916 | { |
917 | g_object_ref (expander->child); |
918 | gtk_box_remove (GTK_BOX (expander->box), child); |
919 | } |
920 | gtk_expander_resize_toplevel (expander); |
921 | } |
922 | |
923 | gtk_accessible_update_state (self: GTK_ACCESSIBLE (ptr: expander), |
924 | first_state: GTK_ACCESSIBLE_STATE_EXPANDED, expanded, |
925 | -1); |
926 | |
927 | g_object_notify (G_OBJECT (expander), property_name: "expanded" ); |
928 | } |
929 | |
930 | /** |
931 | * gtk_expander_get_expanded: (attributes org.gtk.Method.get_property=expanded) |
932 | * @expander:a `GtkExpander` |
933 | * |
934 | * Queries a `GtkExpander` and returns its current state. |
935 | * |
936 | * Returns %TRUE if the child widget is revealed. |
937 | * |
938 | * Returns: the current state of the expander |
939 | */ |
940 | gboolean |
941 | gtk_expander_get_expanded (GtkExpander *expander) |
942 | { |
943 | g_return_val_if_fail (GTK_IS_EXPANDER (expander), FALSE); |
944 | |
945 | return expander->expanded; |
946 | } |
947 | |
948 | /** |
949 | * gtk_expander_set_label: (attributes org.gtk.Method.set_property=label) |
950 | * @expander: a `GtkExpander` |
951 | * @label: (nullable): a string |
952 | * |
953 | * Sets the text of the label of the expander to @label. |
954 | * |
955 | * This will also clear any previously set labels. |
956 | */ |
957 | void |
958 | gtk_expander_set_label (GtkExpander *expander, |
959 | const char *label) |
960 | { |
961 | g_return_if_fail (GTK_IS_EXPANDER (expander)); |
962 | |
963 | if (!label) |
964 | { |
965 | gtk_expander_set_label_widget (expander, NULL); |
966 | } |
967 | else |
968 | { |
969 | GtkWidget *child; |
970 | |
971 | child = gtk_label_new (str: label); |
972 | gtk_label_set_use_underline (GTK_LABEL (child), setting: expander->use_underline); |
973 | gtk_label_set_use_markup (GTK_LABEL (child), setting: expander->use_markup); |
974 | gtk_widget_show (widget: child); |
975 | |
976 | gtk_expander_set_label_widget (expander, label_widget: child); |
977 | } |
978 | |
979 | g_object_notify (G_OBJECT (expander), property_name: "label" ); |
980 | } |
981 | |
982 | /** |
983 | * gtk_expander_get_label: (attributes org.gtk.Method.get_property=label) |
984 | * @expander: a `GtkExpander` |
985 | * |
986 | * Fetches the text from a label widget. |
987 | * |
988 | * This is including any embedded underlines indicating mnemonics and |
989 | * Pango markup, as set by [method@Gtk.Expander.set_label]. If the label |
990 | * text has not been set the return value will be %NULL. This will be the |
991 | * case if you create an empty button with gtk_button_new() to use as a |
992 | * container. |
993 | * |
994 | * Returns: (nullable): The text of the label widget. This string is owned |
995 | * by the widget and must not be modified or freed. |
996 | */ |
997 | const char * |
998 | gtk_expander_get_label (GtkExpander *expander) |
999 | { |
1000 | g_return_val_if_fail (GTK_IS_EXPANDER (expander), NULL); |
1001 | |
1002 | if (GTK_IS_LABEL (expander->label_widget)) |
1003 | return gtk_label_get_label (GTK_LABEL (expander->label_widget)); |
1004 | else |
1005 | return NULL; |
1006 | } |
1007 | |
1008 | /** |
1009 | * gtk_expander_set_use_underline: (attributes org.gtk.Method.set_property=use-underline) |
1010 | * @expander: a `GtkExpander` |
1011 | * @use_underline: %TRUE if underlines in the text indicate mnemonics |
1012 | * |
1013 | * If true, an underline in the text indicates a mnemonic. |
1014 | */ |
1015 | void |
1016 | gtk_expander_set_use_underline (GtkExpander *expander, |
1017 | gboolean use_underline) |
1018 | { |
1019 | g_return_if_fail (GTK_IS_EXPANDER (expander)); |
1020 | |
1021 | use_underline = use_underline != FALSE; |
1022 | |
1023 | if (expander->use_underline != use_underline) |
1024 | { |
1025 | expander->use_underline = use_underline; |
1026 | |
1027 | if (GTK_IS_LABEL (expander->label_widget)) |
1028 | gtk_label_set_use_underline (GTK_LABEL (expander->label_widget), setting: use_underline); |
1029 | |
1030 | g_object_notify (G_OBJECT (expander), property_name: "use-underline" ); |
1031 | } |
1032 | } |
1033 | |
1034 | /** |
1035 | * gtk_expander_get_use_underline: (attributes org.gtk.Method.get_property=use-underline) |
1036 | * @expander: a `GtkExpander` |
1037 | * |
1038 | * Returns whether an underline in the text indicates a mnemonic. |
1039 | * |
1040 | * Returns: %TRUE if an embedded underline in the expander |
1041 | * label indicates the mnemonic accelerator keys |
1042 | */ |
1043 | gboolean |
1044 | gtk_expander_get_use_underline (GtkExpander *expander) |
1045 | { |
1046 | g_return_val_if_fail (GTK_IS_EXPANDER (expander), FALSE); |
1047 | |
1048 | return expander->use_underline; |
1049 | } |
1050 | |
1051 | /** |
1052 | * gtk_expander_set_use_markup: (attributes org.gtk.Method.set_property=use-markup) |
1053 | * @expander: a `GtkExpander` |
1054 | * @use_markup: %TRUE if the label’s text should be parsed for markup |
1055 | * |
1056 | * Sets whether the text of the label contains Pango markup. |
1057 | */ |
1058 | void |
1059 | gtk_expander_set_use_markup (GtkExpander *expander, |
1060 | gboolean use_markup) |
1061 | { |
1062 | g_return_if_fail (GTK_IS_EXPANDER (expander)); |
1063 | |
1064 | use_markup = use_markup != FALSE; |
1065 | |
1066 | if (expander->use_markup != use_markup) |
1067 | { |
1068 | expander->use_markup = use_markup; |
1069 | |
1070 | if (GTK_IS_LABEL (expander->label_widget)) |
1071 | gtk_label_set_use_markup (GTK_LABEL (expander->label_widget), setting: use_markup); |
1072 | |
1073 | g_object_notify (G_OBJECT (expander), property_name: "use-markup" ); |
1074 | } |
1075 | } |
1076 | |
1077 | /** |
1078 | * gtk_expander_get_use_markup: (attributes org.gtk.Method.get_property=use-markup) |
1079 | * @expander: a `GtkExpander` |
1080 | * |
1081 | * Returns whether the label’s text is interpreted as Pango markup. |
1082 | * |
1083 | * Returns: %TRUE if the label’s text will be parsed for markup |
1084 | */ |
1085 | gboolean |
1086 | gtk_expander_get_use_markup (GtkExpander *expander) |
1087 | { |
1088 | g_return_val_if_fail (GTK_IS_EXPANDER (expander), FALSE); |
1089 | |
1090 | return expander->use_markup; |
1091 | } |
1092 | |
1093 | /** |
1094 | * gtk_expander_set_label_widget: (attributes org.gtk.Method.set_property=label-widget) |
1095 | * @expander: a `GtkExpander` |
1096 | * @label_widget: (nullable): the new label widget |
1097 | * |
1098 | * Set the label widget for the expander. |
1099 | * |
1100 | * This is the widget that will appear embedded alongside |
1101 | * the expander arrow. |
1102 | */ |
1103 | void |
1104 | gtk_expander_set_label_widget (GtkExpander *expander, |
1105 | GtkWidget *label_widget) |
1106 | { |
1107 | GtkWidget *widget; |
1108 | |
1109 | g_return_if_fail (GTK_IS_EXPANDER (expander)); |
1110 | g_return_if_fail (label_widget == NULL || GTK_IS_WIDGET (label_widget)); |
1111 | g_return_if_fail (label_widget == NULL || gtk_widget_get_parent (label_widget) == NULL); |
1112 | |
1113 | if (expander->label_widget == label_widget) |
1114 | return; |
1115 | |
1116 | if (expander->label_widget) |
1117 | gtk_box_remove (GTK_BOX (expander->title_widget), child: expander->label_widget); |
1118 | |
1119 | expander->label_widget = label_widget; |
1120 | widget = GTK_WIDGET (expander); |
1121 | |
1122 | if (label_widget) |
1123 | { |
1124 | expander->label_widget = label_widget; |
1125 | |
1126 | gtk_box_append (GTK_BOX (expander->title_widget), child: label_widget); |
1127 | } |
1128 | |
1129 | if (gtk_widget_get_visible (widget)) |
1130 | gtk_widget_queue_resize (widget); |
1131 | |
1132 | g_object_freeze_notify (G_OBJECT (expander)); |
1133 | g_object_notify (G_OBJECT (expander), property_name: "label-widget" ); |
1134 | g_object_notify (G_OBJECT (expander), property_name: "label" ); |
1135 | g_object_thaw_notify (G_OBJECT (expander)); |
1136 | } |
1137 | |
1138 | /** |
1139 | * gtk_expander_get_label_widget: (attributes org.gtk.Method.get_property=label-widget) |
1140 | * @expander: a `GtkExpander` |
1141 | * |
1142 | * Retrieves the label widget for the frame. |
1143 | * |
1144 | * Returns: (nullable) (transfer none): the label widget |
1145 | */ |
1146 | GtkWidget * |
1147 | gtk_expander_get_label_widget (GtkExpander *expander) |
1148 | { |
1149 | g_return_val_if_fail (GTK_IS_EXPANDER (expander), NULL); |
1150 | |
1151 | return expander->label_widget; |
1152 | } |
1153 | |
1154 | /** |
1155 | * gtk_expander_set_resize_toplevel: (attributes org.gtk.Method.set_property=resize-toplevel) |
1156 | * @expander: a `GtkExpander` |
1157 | * @resize_toplevel: whether to resize the toplevel |
1158 | * |
1159 | * Sets whether the expander will resize the toplevel widget |
1160 | * containing the expander upon resizing and collpasing. |
1161 | */ |
1162 | void |
1163 | gtk_expander_set_resize_toplevel (GtkExpander *expander, |
1164 | gboolean resize_toplevel) |
1165 | { |
1166 | g_return_if_fail (GTK_IS_EXPANDER (expander)); |
1167 | |
1168 | if (expander->resize_toplevel != resize_toplevel) |
1169 | { |
1170 | expander->resize_toplevel = resize_toplevel ? TRUE : FALSE; |
1171 | g_object_notify (G_OBJECT (expander), property_name: "resize-toplevel" ); |
1172 | } |
1173 | } |
1174 | |
1175 | /** |
1176 | * gtk_expander_get_resize_toplevel: (attributes org.gtk.Method.get_property=resize-toplevel) |
1177 | * @expander: a `GtkExpander` |
1178 | * |
1179 | * Returns whether the expander will resize the toplevel widget |
1180 | * containing the expander upon resizing and collpasing. |
1181 | * |
1182 | * Returns: the “resize toplevel” setting. |
1183 | */ |
1184 | gboolean |
1185 | gtk_expander_get_resize_toplevel (GtkExpander *expander) |
1186 | { |
1187 | g_return_val_if_fail (GTK_IS_EXPANDER (expander), FALSE); |
1188 | |
1189 | return expander->resize_toplevel; |
1190 | } |
1191 | |
1192 | /** |
1193 | * gtk_expander_set_child: (attributes org.gtk.Method.set_property=child) |
1194 | * @expander: a `GtkExpander` |
1195 | * @child: (nullable): the child widget |
1196 | * |
1197 | * Sets the child widget of @expander. |
1198 | */ |
1199 | void |
1200 | gtk_expander_set_child (GtkExpander *expander, |
1201 | GtkWidget *child) |
1202 | { |
1203 | g_return_if_fail (GTK_IS_EXPANDER (expander)); |
1204 | g_return_if_fail (child == NULL || GTK_IS_WIDGET (child)); |
1205 | |
1206 | if (expander->child == child) |
1207 | return; |
1208 | |
1209 | if (expander->child) |
1210 | { |
1211 | if (!expander->expanded) |
1212 | g_object_unref (object: expander->child); |
1213 | else |
1214 | gtk_box_remove (GTK_BOX (expander->box), child: expander->child); |
1215 | } |
1216 | |
1217 | expander->child = child; |
1218 | |
1219 | if (expander->child) |
1220 | { |
1221 | /* We only add the child to the box if the expander is |
1222 | * expanded; otherwise we just claim ownership of the |
1223 | * child by sinking its floating reference, or acquiring |
1224 | * an additional reference to it. The reference will be |
1225 | * dropped once the expander is expanded |
1226 | */ |
1227 | if (expander->expanded) |
1228 | gtk_box_append (GTK_BOX (expander->box), child: expander->child); |
1229 | else |
1230 | g_object_ref_sink (expander->child); |
1231 | |
1232 | gtk_accessible_update_relation (self: GTK_ACCESSIBLE (ptr: expander), |
1233 | first_relation: GTK_ACCESSIBLE_RELATION_CONTROLS, expander->child, NULL, |
1234 | -1); |
1235 | } |
1236 | else |
1237 | { |
1238 | gtk_accessible_reset_relation (self: GTK_ACCESSIBLE (ptr: expander), |
1239 | relation: GTK_ACCESSIBLE_RELATION_CONTROLS); |
1240 | } |
1241 | |
1242 | g_object_notify (G_OBJECT (expander), property_name: "child" ); |
1243 | } |
1244 | |
1245 | /** |
1246 | * gtk_expander_get_child: (attributes org.gtk.Method.get_property=child) |
1247 | * @expander: a `GtkExpander` |
1248 | * |
1249 | * Gets the child widget of @expander. |
1250 | * |
1251 | * Returns: (nullable) (transfer none): the child widget of @expander |
1252 | */ |
1253 | GtkWidget * |
1254 | gtk_expander_get_child (GtkExpander *expander) |
1255 | { |
1256 | g_return_val_if_fail (GTK_IS_EXPANDER (expander), NULL); |
1257 | |
1258 | return expander->child; |
1259 | } |
1260 | |
1261 | |