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-2001. 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/**
26 * GtkButton:
27 *
28 * The `GtkButton` widget is generally used to trigger a callback function that is
29 * called when the button is pressed.
30 *
31 * ![An example GtkButton](button.png)
32 *
33 * The `GtkButton` widget can hold any valid child widget. That is, it can hold
34 * almost any other standard `GtkWidget`. The most commonly used child is the
35 * `GtkLabel`.
36 *
37 * # CSS nodes
38 *
39 * `GtkButton` has a single CSS node with name button. The node will get the
40 * style classes .image-button or .text-button, if the content is just an
41 * image or label, respectively. It may also receive the .flat style class.
42 * When activating a button via the keyboard, the button will temporarily
43 * gain the .keyboard-activating style class.
44 *
45 * Other style classes that are commonly used with `GtkButton` include
46 * .suggested-action and .destructive-action. In special cases, buttons
47 * can be made round by adding the .circular style class.
48 *
49 * Button-like widgets like [class@Gtk.ToggleButton], [class@Gtk.MenuButton],
50 * [class@Gtk.VolumeButton], [class@Gtk.LockButton], [class@Gtk.ColorButton]
51 * or [class@Gtk.FontButton] use style classes such as .toggle, .popup, .scale,
52 * .lock, .color on the button node to differentiate themselves from a plain
53 * `GtkButton`.
54 *
55 * # Accessibility
56 *
57 * `GtkButton` uses the %GTK_ACCESSIBLE_ROLE_BUTTON role.
58 */
59
60#include "config.h"
61
62#include "gtkbuttonprivate.h"
63
64#include "gtkactionhelperprivate.h"
65#include "gtkbuildable.h"
66#include "gtkcheckbutton.h"
67#include "gtkgestureclick.h"
68#include "gtkeventcontrollerkey.h"
69#include "gtkbinlayout.h"
70#include "gtkimage.h"
71#include "gtkintl.h"
72#include "gtklabel.h"
73#include "gtkmain.h"
74#include "gtkmarshalers.h"
75#include "gtkprivate.h"
76#include "gtktypebuiltins.h"
77#include "gtkwidgetprivate.h"
78#include "gtkshortcuttrigger.h"
79
80#include <string.h>
81
82/* Time out before giving up on getting a key release when animating
83 * the close button.
84 */
85#define ACTIVATE_TIMEOUT 250
86
87struct _GtkButtonPrivate
88{
89 GtkWidget *child;
90
91 GtkActionHelper *action_helper;
92
93 GtkGesture *gesture;
94
95 guint activate_timeout;
96
97 guint button_down : 1;
98 guint use_underline : 1;
99 guint child_type : 2;
100};
101
102enum {
103 CLICKED,
104 ACTIVATE,
105 LAST_SIGNAL
106};
107
108enum {
109 PROP_0,
110 PROP_LABEL,
111 PROP_HAS_FRAME,
112 PROP_USE_UNDERLINE,
113 PROP_ICON_NAME,
114 PROP_CHILD,
115
116 /* actionable properties */
117 PROP_ACTION_NAME,
118 PROP_ACTION_TARGET,
119 LAST_PROP = PROP_ACTION_NAME
120};
121
122enum {
123 LABEL_CHILD,
124 ICON_CHILD,
125 WIDGET_CHILD
126};
127
128
129static void gtk_button_dispose (GObject *object);
130static void gtk_button_set_property (GObject *object,
131 guint prop_id,
132 const GValue *value,
133 GParamSpec *pspec);
134static void gtk_button_get_property (GObject *object,
135 guint prop_id,
136 GValue *value,
137 GParamSpec *pspec);
138static void gtk_button_unrealize (GtkWidget * widget);
139static void gtk_real_button_clicked (GtkButton * button);
140static void gtk_real_button_activate (GtkButton *button);
141static void gtk_button_finish_activate (GtkButton *button,
142 gboolean do_it);
143
144static void gtk_button_state_flags_changed (GtkWidget *widget,
145 GtkStateFlags previous_state);
146static void gtk_button_do_release (GtkButton *button,
147 gboolean emit_clicked);
148static void gtk_button_set_child_type (GtkButton *button, guint child_type);
149
150static void gtk_button_buildable_iface_init (GtkBuildableIface *iface);
151static void gtk_button_actionable_iface_init (GtkActionableInterface *iface);
152
153static GParamSpec *props[LAST_PROP] = { NULL, };
154static guint button_signals[LAST_SIGNAL] = { 0 };
155
156G_DEFINE_TYPE_WITH_CODE (GtkButton, gtk_button, GTK_TYPE_WIDGET,
157 G_ADD_PRIVATE (GtkButton)
158 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, gtk_button_buildable_iface_init)
159 G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIONABLE, gtk_button_actionable_iface_init))
160
161static void
162gtk_button_compute_expand (GtkWidget *widget,
163 gboolean *hexpand,
164 gboolean *vexpand)
165{
166 GtkButtonPrivate *priv = gtk_button_get_instance_private (GTK_BUTTON (widget));
167
168 if (priv->child)
169 {
170 *hexpand = gtk_widget_compute_expand (widget: priv->child, orientation: GTK_ORIENTATION_HORIZONTAL);
171 *vexpand = gtk_widget_compute_expand (widget: priv->child, orientation: GTK_ORIENTATION_VERTICAL);
172 }
173 else
174 {
175 *hexpand = FALSE;
176 *vexpand = FALSE;
177 }
178}
179
180static GtkSizeRequestMode
181gtk_button_get_request_mode (GtkWidget *widget)
182{
183 GtkButtonPrivate *priv = gtk_button_get_instance_private (GTK_BUTTON (widget));
184
185 if (priv->child)
186 return gtk_widget_get_request_mode (widget: priv->child);
187 else
188 return GTK_SIZE_REQUEST_CONSTANT_SIZE;
189}
190
191static void
192gtk_button_class_init (GtkButtonClass *klass)
193{
194 const guint activate_keyvals[] = { GDK_KEY_space, GDK_KEY_KP_Space, GDK_KEY_Return,
195 GDK_KEY_ISO_Enter, GDK_KEY_KP_Enter };
196 GObjectClass *gobject_class;
197 GtkWidgetClass *widget_class;
198 GtkShortcutAction *activate_action;
199
200 gobject_class = G_OBJECT_CLASS (klass);
201 widget_class = (GtkWidgetClass*) klass;
202
203 gobject_class->dispose = gtk_button_dispose;
204 gobject_class->set_property = gtk_button_set_property;
205 gobject_class->get_property = gtk_button_get_property;
206
207 widget_class->unrealize = gtk_button_unrealize;
208 widget_class->state_flags_changed = gtk_button_state_flags_changed;
209 widget_class->compute_expand = gtk_button_compute_expand;
210 widget_class->get_request_mode = gtk_button_get_request_mode;
211
212 klass->clicked = NULL;
213 klass->activate = gtk_real_button_activate;
214
215 /**
216 * GtkButton:label: (attributes org.gtk.Property.get=gtk_button_get_label org.gtk.Property.set=gtk_button_set_label)
217 *
218 * Text of the label inside the button, if the button contains a label widget.
219 */
220 props[PROP_LABEL] =
221 g_param_spec_string (name: "label",
222 P_("Label"),
223 P_("Text of the label widget inside the button, if the button contains a label widget"),
224 NULL,
225 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
226
227 /**
228 * GtkButton:use-underline: (attributes org.gtk.Property.get=gtk_button_get_use_underline org.gtk.Property.set=gtk_button_set_use_underline)
229 *
230 * If set, an underline in the text indicates that the following character is
231 * to be used as mnemonic.
232 */
233 props[PROP_USE_UNDERLINE] =
234 g_param_spec_boolean (name: "use-underline",
235 P_("Use underline"),
236 P_("If set, an underline in the text indicates the next character should be used for the mnemonic accelerator key"),
237 FALSE,
238 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
239
240 /**
241 * GtkButton:has-frame: (attributes org.gtk.Property.get=gtk_button_get_has_frame org.gtk.Property.set=gtk_button_set_has_frame)
242 *
243 * Whether the button has a frame.
244 */
245 props[PROP_HAS_FRAME] =
246 g_param_spec_boolean (name: "has-frame",
247 P_("Has Frame"),
248 P_("Whether the button has a frame"),
249 TRUE,
250 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
251
252 /**
253 * GtkButton:icon-name: (attributes org.gtk.Property.get=gtk_button_get_icon_name org.gtk.Property.set=gtk_button_set_icon_name)
254 *
255 * The name of the icon used to automatically populate the button.
256 */
257 props[PROP_ICON_NAME] =
258 g_param_spec_string (name: "icon-name",
259 P_("Icon Name"),
260 P_("The name of the icon used to automatically populate the button"),
261 NULL,
262 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
263
264 /**
265 * GtkButton:child: (attributes org.gtk.Property.get=gtk_button_get_child org.gtk.Property.set=gtk_button_set_child)
266 *
267 * The child widget.
268 */
269 props[PROP_CHILD] =
270 g_param_spec_object (name: "child",
271 P_("Child"),
272 P_("The child widget"),
273 GTK_TYPE_WIDGET,
274 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
275
276 g_object_class_install_properties (oclass: gobject_class, n_pspecs: LAST_PROP, pspecs: props);
277
278 g_object_class_override_property (oclass: gobject_class, property_id: PROP_ACTION_NAME, name: "action-name");
279 g_object_class_override_property (oclass: gobject_class, property_id: PROP_ACTION_TARGET, name: "action-target");
280
281 /**
282 * GtkButton::clicked:
283 * @button: the object that received the signal
284 *
285 * Emitted when the button has been activated (pressed and released).
286 */
287 button_signals[CLICKED] =
288 g_signal_new (I_("clicked"),
289 G_OBJECT_CLASS_TYPE (gobject_class),
290 signal_flags: G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
291 G_STRUCT_OFFSET (GtkButtonClass, clicked),
292 NULL, NULL,
293 NULL,
294 G_TYPE_NONE, n_params: 0);
295
296 /**
297 * GtkButton::activate:
298 * @widget: the object which received the signal.
299 *
300 * Emitted to animate press then release.
301 *
302 * This is an action signal. Applications should never connect
303 * to this signal, but use the [signal@Gtk.Button::clicked] signal.
304 */
305 button_signals[ACTIVATE] =
306 g_signal_new (I_("activate"),
307 G_OBJECT_CLASS_TYPE (gobject_class),
308 signal_flags: G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
309 G_STRUCT_OFFSET (GtkButtonClass, activate),
310 NULL, NULL,
311 NULL,
312 G_TYPE_NONE, n_params: 0);
313
314 gtk_widget_class_set_activate_signal (widget_class, signal_id: button_signals[ACTIVATE]);
315
316 activate_action = gtk_signal_action_new (signal_name: "activate");
317 for (guint i = 0; i < G_N_ELEMENTS (activate_keyvals); i++)
318 {
319 GtkShortcut *activate_shortcut = gtk_shortcut_new (trigger: gtk_keyval_trigger_new (keyval: activate_keyvals[i], modifiers: 0),
320 g_object_ref (activate_action));
321
322 gtk_widget_class_add_shortcut (widget_class, shortcut: activate_shortcut);
323 g_object_unref (object: activate_shortcut);
324 }
325 g_object_unref (object: activate_action);
326
327 gtk_widget_class_set_accessible_role (widget_class, accessible_role: GTK_ACCESSIBLE_ROLE_BUTTON);
328 gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
329 gtk_widget_class_set_css_name (widget_class, I_("button"));
330}
331
332static void
333click_pressed_cb (GtkGestureClick *gesture,
334 guint n_press,
335 double x,
336 double y,
337 GtkWidget *widget)
338{
339 GtkButton *button = GTK_BUTTON (widget);
340 GtkButtonPrivate *priv = gtk_button_get_instance_private (self: button);
341
342 if (gtk_widget_get_focus_on_click (widget) && !gtk_widget_has_focus (widget))
343 gtk_widget_grab_focus (widget);
344
345 if (!priv->activate_timeout)
346 priv->button_down = TRUE;
347}
348
349static void
350click_released_cb (GtkGestureClick *gesture,
351 guint n_press,
352 double x,
353 double y,
354 GtkWidget *widget)
355{
356 GtkButton *button = GTK_BUTTON (widget);
357
358 gtk_gesture_set_state (GTK_GESTURE (gesture), state: GTK_EVENT_SEQUENCE_CLAIMED);
359 gtk_button_do_release (button,
360 emit_clicked: gtk_widget_is_sensitive (GTK_WIDGET (button)) &&
361 gtk_widget_contains (widget, x, y));
362}
363
364static void
365click_gesture_cancel_cb (GtkGesture *gesture,
366 GdkEventSequence *sequence,
367 GtkButton *button)
368{
369 GtkButtonPrivate *priv = gtk_button_get_instance_private (self: button);
370
371 if (priv->activate_timeout)
372 gtk_button_finish_activate (button, FALSE);
373
374 gtk_button_do_release (button, FALSE);
375}
376
377static gboolean
378key_controller_key_pressed_cb (GtkEventControllerKey *controller,
379 guint keyval,
380 guint keycode,
381 guint modifiers,
382 GtkButton *button)
383{
384 GtkButtonPrivate *priv = gtk_button_get_instance_private (self: button);
385
386 return priv->activate_timeout != 0;
387}
388
389static void
390key_controller_key_released_cb (GtkEventControllerKey *controller,
391 guint keyval,
392 guint keycode,
393 guint modifiers,
394 GtkButton *button)
395{
396 GtkButtonPrivate *priv = gtk_button_get_instance_private (self: button);
397
398 if (priv->activate_timeout)
399 gtk_button_finish_activate (button, TRUE);
400}
401
402static void
403gtk_button_set_child_type (GtkButton *button, guint child_type)
404{
405 GtkButtonPrivate *priv = gtk_button_get_instance_private (self: button);
406
407 if (priv->child_type == child_type)
408 return;
409
410 if (child_type == LABEL_CHILD)
411 gtk_widget_add_css_class (GTK_WIDGET (button), css_class: "text-button");
412 else
413 gtk_widget_remove_css_class (GTK_WIDGET (button), css_class: "text-button");
414
415 if (child_type == ICON_CHILD)
416 gtk_widget_add_css_class (GTK_WIDGET (button), css_class: "image-button");
417 else
418 gtk_widget_remove_css_class (GTK_WIDGET (button), css_class: "image-button");
419
420 if (child_type != LABEL_CHILD)
421 g_object_notify_by_pspec (G_OBJECT (button), pspec: props[PROP_LABEL]);
422 else if (child_type != ICON_CHILD)
423 g_object_notify_by_pspec (G_OBJECT (button), pspec: props[PROP_ICON_NAME]);
424
425 priv->child_type = child_type;
426}
427
428static void
429gtk_button_init (GtkButton *button)
430{
431 GtkButtonPrivate *priv = gtk_button_get_instance_private (self: button);
432 GtkEventController *key_controller;
433
434 gtk_widget_set_focusable (GTK_WIDGET (button), TRUE);
435 gtk_widget_set_receives_default (GTK_WIDGET (button), TRUE);
436
437 priv->button_down = FALSE;
438 priv->use_underline = FALSE;
439 priv->child_type = WIDGET_CHILD;
440
441 priv->gesture = gtk_gesture_click_new ();
442 gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (priv->gesture), FALSE);
443 gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (priv->gesture), GDK_BUTTON_PRIMARY);
444 g_signal_connect (priv->gesture, "pressed", G_CALLBACK (click_pressed_cb), button);
445 g_signal_connect (priv->gesture, "released", G_CALLBACK (click_released_cb), button);
446 g_signal_connect (priv->gesture, "cancel", G_CALLBACK (click_gesture_cancel_cb), button);
447 gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->gesture), phase: GTK_PHASE_CAPTURE);
448 gtk_widget_add_controller (GTK_WIDGET (button), GTK_EVENT_CONTROLLER (priv->gesture));
449
450 key_controller = gtk_event_controller_key_new ();
451 g_signal_connect (key_controller, "key-pressed", G_CALLBACK (key_controller_key_pressed_cb), button);
452 g_signal_connect (key_controller, "key-released", G_CALLBACK (key_controller_key_released_cb), button);
453 gtk_widget_add_controller (GTK_WIDGET (button), controller: key_controller);
454}
455
456static void
457gtk_button_dispose (GObject *object)
458{
459 GtkButton *button = GTK_BUTTON (object);
460 GtkButtonPrivate *priv = gtk_button_get_instance_private (self: button);
461
462 g_clear_pointer (&priv->child, gtk_widget_unparent);
463 g_clear_object (&priv->action_helper);
464
465 G_OBJECT_CLASS (gtk_button_parent_class)->dispose (object);
466}
467
468static void
469gtk_button_set_action_name (GtkActionable *actionable,
470 const char *action_name)
471{
472 GtkButton *button = GTK_BUTTON (actionable);
473 GtkButtonPrivate *priv = gtk_button_get_instance_private (self: button);
474
475 if (!priv->action_helper)
476 priv->action_helper = gtk_action_helper_new (widget: actionable);
477
478 g_signal_handlers_disconnect_by_func (button, gtk_real_button_clicked, NULL);
479 if (action_name)
480 g_signal_connect_after (button, "clicked", G_CALLBACK (gtk_real_button_clicked), NULL);
481
482 gtk_action_helper_set_action_name (helper: priv->action_helper, action_name);
483}
484
485static void
486gtk_button_set_action_target_value (GtkActionable *actionable,
487 GVariant *action_target)
488{
489 GtkButton *button = GTK_BUTTON (actionable);
490 GtkButtonPrivate *priv = gtk_button_get_instance_private (self: button);
491
492 if (!priv->action_helper)
493 priv->action_helper = gtk_action_helper_new (widget: actionable);
494
495 gtk_action_helper_set_action_target_value (helper: priv->action_helper, action_target);
496}
497
498static void
499gtk_button_set_property (GObject *object,
500 guint prop_id,
501 const GValue *value,
502 GParamSpec *pspec)
503{
504 GtkButton *button = GTK_BUTTON (object);
505
506 switch (prop_id)
507 {
508 case PROP_LABEL:
509 gtk_button_set_label (button, label: g_value_get_string (value));
510 break;
511 case PROP_HAS_FRAME:
512 gtk_button_set_has_frame (button, has_frame: g_value_get_boolean (value));
513 break;
514 case PROP_USE_UNDERLINE:
515 gtk_button_set_use_underline (button, use_underline: g_value_get_boolean (value));
516 break;
517 case PROP_ICON_NAME:
518 gtk_button_set_icon_name (button, icon_name: g_value_get_string (value));
519 break;
520 case PROP_CHILD:
521 gtk_button_set_child (button, child: g_value_get_object (value));
522 break;
523 case PROP_ACTION_NAME:
524 gtk_button_set_action_name (GTK_ACTIONABLE (button), action_name: g_value_get_string (value));
525 break;
526 case PROP_ACTION_TARGET:
527 gtk_button_set_action_target_value (GTK_ACTIONABLE (button), action_target: g_value_get_variant (value));
528 break;
529 default:
530 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
531 break;
532 }
533}
534
535static void
536gtk_button_get_property (GObject *object,
537 guint prop_id,
538 GValue *value,
539 GParamSpec *pspec)
540{
541 GtkButton *button = GTK_BUTTON (object);
542 GtkButtonPrivate *priv = gtk_button_get_instance_private (self: button);
543
544 switch (prop_id)
545 {
546 case PROP_LABEL:
547 g_value_set_string (value, v_string: gtk_button_get_label (button));
548 break;
549 case PROP_HAS_FRAME:
550 g_value_set_boolean (value, v_boolean: gtk_button_get_has_frame (button));
551 break;
552 case PROP_USE_UNDERLINE:
553 g_value_set_boolean (value, v_boolean: priv->use_underline);
554 break;
555 case PROP_ICON_NAME:
556 g_value_set_string (value, v_string: gtk_button_get_icon_name (button));
557 break;
558 case PROP_CHILD:
559 g_value_set_object (value, v_object: priv->child);
560 break;
561 case PROP_ACTION_NAME:
562 g_value_set_string (value, v_string: gtk_action_helper_get_action_name (helper: priv->action_helper));
563 break;
564 case PROP_ACTION_TARGET:
565 g_value_set_variant (value, variant: gtk_action_helper_get_action_target_value (helper: priv->action_helper));
566 break;
567 default:
568 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
569 break;
570 }
571}
572
573static const char *
574gtk_button_get_action_name (GtkActionable *actionable)
575{
576 GtkButton *button = GTK_BUTTON (actionable);
577 GtkButtonPrivate *priv = gtk_button_get_instance_private (self: button);
578
579 return gtk_action_helper_get_action_name (helper: priv->action_helper);
580}
581
582static GVariant *
583gtk_button_get_action_target_value (GtkActionable *actionable)
584{
585 GtkButton *button = GTK_BUTTON (actionable);
586 GtkButtonPrivate *priv = gtk_button_get_instance_private (self: button);
587
588 return gtk_action_helper_get_action_target_value (helper: priv->action_helper);
589}
590
591static void
592gtk_button_actionable_iface_init (GtkActionableInterface *iface)
593{
594 iface->get_action_name = gtk_button_get_action_name;
595 iface->set_action_name = gtk_button_set_action_name;
596 iface->get_action_target_value = gtk_button_get_action_target_value;
597 iface->set_action_target_value = gtk_button_set_action_target_value;
598}
599
600static GtkBuildableIface *parent_buildable_iface;
601
602static void
603gtk_button_buildable_add_child (GtkBuildable *buildable,
604 GtkBuilder *builder,
605 GObject *child,
606 const char *type)
607{
608 if (GTK_IS_WIDGET (child))
609 gtk_button_set_child (GTK_BUTTON (buildable), GTK_WIDGET (child));
610 else
611 parent_buildable_iface->add_child (buildable, builder, child, type);
612}
613
614static void
615gtk_button_buildable_iface_init (GtkBuildableIface *iface)
616{
617 parent_buildable_iface = g_type_interface_peek_parent (g_iface: iface);
618
619 iface->add_child = gtk_button_buildable_add_child;
620}
621
622/**
623 * gtk_button_new:
624 *
625 * Creates a new `GtkButton` widget.
626 *
627 * To add a child widget to the button, use [method@Gtk.Button.set_child].
628 *
629 * Returns: The newly created `GtkButton` widget.
630 */
631GtkWidget*
632gtk_button_new (void)
633{
634 return g_object_new (GTK_TYPE_BUTTON, NULL);
635}
636
637/**
638 * gtk_button_new_with_label:
639 * @label: The text you want the `GtkLabel` to hold
640 *
641 * Creates a `GtkButton` widget with a `GtkLabel` child.
642 *
643 * Returns: The newly created `GtkButton` widget
644 */
645GtkWidget*
646gtk_button_new_with_label (const char *label)
647{
648 return g_object_new (GTK_TYPE_BUTTON, first_property_name: "label", label, NULL);
649}
650
651/**
652 * gtk_button_new_from_icon_name:
653 * @icon_name: (nullable): an icon name
654 *
655 * Creates a new button containing an icon from the current icon theme.
656 *
657 * If the icon name isn’t known, a “broken image” icon will be
658 * displayed instead. If the current icon theme is changed, the icon
659 * will be updated appropriately.
660 *
661 * Returns: a new `GtkButton` displaying the themed icon
662 */
663GtkWidget*
664gtk_button_new_from_icon_name (const char *icon_name)
665{
666 GtkWidget *button;
667
668 button = g_object_new (GTK_TYPE_BUTTON,
669 first_property_name: "icon-name", icon_name,
670 NULL);
671
672 return button;
673}
674
675/**
676 * gtk_button_new_with_mnemonic:
677 * @label: The text of the button, with an underscore in front of the
678 * mnemonic character
679 *
680 * Creates a new `GtkButton` containing a label.
681 *
682 * If characters in @label are preceded by an underscore, they are underlined.
683 * If you need a literal underscore character in a label, use “__” (two
684 * underscores). The first underlined character represents a keyboard
685 * accelerator called a mnemonic. Pressing Alt and that key activates the button.
686 *
687 * Returns: a new `GtkButton`
688 */
689GtkWidget*
690gtk_button_new_with_mnemonic (const char *label)
691{
692 return g_object_new (GTK_TYPE_BUTTON, first_property_name: "label", label, "use-underline", TRUE, NULL);
693}
694
695/**
696 * gtk_button_set_has_frame: (attributes org.gtk.Method.set_property=has-frame)
697 * @button: a `GtkButton`
698 * @has_frame: whether the button should have a visible frame
699 *
700 * Sets the style of the button.
701 *
702 * Buttons can has a flat appearance or have a frame drawn around them.
703 */
704void
705gtk_button_set_has_frame (GtkButton *button,
706 gboolean has_frame)
707{
708
709 g_return_if_fail (GTK_IS_BUTTON (button));
710
711 if (gtk_button_get_has_frame (button) == has_frame)
712 return;
713
714 if (has_frame)
715 gtk_widget_remove_css_class (GTK_WIDGET (button), css_class: "flat");
716 else
717 gtk_widget_add_css_class (GTK_WIDGET (button), css_class: "flat");
718
719 g_object_notify_by_pspec (G_OBJECT (button), pspec: props[PROP_HAS_FRAME]);
720}
721
722/**
723 * gtk_button_get_has_frame: (attributes org.gtk.Method.get_property=has-frame)
724 * @button: a `GtkButton`
725 *
726 * Returns whether the button has a frame.
727 *
728 * Returns: %TRUE if the button has a frame
729 */
730gboolean
731gtk_button_get_has_frame (GtkButton *button)
732{
733 g_return_val_if_fail (GTK_IS_BUTTON (button), TRUE);
734
735 return !gtk_widget_has_css_class (GTK_WIDGET (button), css_class: "flat");
736}
737
738static void
739gtk_button_unrealize (GtkWidget *widget)
740{
741 GtkButton *button = GTK_BUTTON (widget);
742 GtkButtonPrivate *priv = gtk_button_get_instance_private (self: button);
743
744 if (priv->activate_timeout)
745 gtk_button_finish_activate (button, FALSE);
746
747 GTK_WIDGET_CLASS (gtk_button_parent_class)->unrealize (widget);
748}
749
750static void
751gtk_button_do_release (GtkButton *button,
752 gboolean emit_clicked)
753{
754 GtkButtonPrivate *priv = gtk_button_get_instance_private (self: button);
755
756 if (priv->button_down)
757 {
758 priv->button_down = FALSE;
759
760 if (priv->activate_timeout)
761 return;
762
763 if (emit_clicked)
764 g_signal_emit (instance: button, signal_id: button_signals[CLICKED], detail: 0);
765 }
766}
767
768static void
769gtk_real_button_clicked (GtkButton *button)
770{
771 GtkButtonPrivate *priv = gtk_button_get_instance_private (self: button);
772
773 if (priv->action_helper)
774 gtk_action_helper_activate (helper: priv->action_helper);
775}
776
777static gboolean
778button_activate_timeout (gpointer data)
779{
780 gtk_button_finish_activate (button: data, TRUE);
781
782 return FALSE;
783}
784
785static void
786gtk_real_button_activate (GtkButton *button)
787{
788 GtkWidget *widget = GTK_WIDGET (button);
789 GtkButtonPrivate *priv = gtk_button_get_instance_private (self: button);
790
791 if (gtk_widget_get_realized (widget) && !priv->activate_timeout)
792 {
793 priv->activate_timeout = g_timeout_add (ACTIVATE_TIMEOUT, function: button_activate_timeout, data: button);
794 gdk_source_set_static_name_by_id (tag: priv->activate_timeout, name: "[gtk] button_activate_timeout");
795
796 gtk_widget_add_css_class (GTK_WIDGET (button), css_class: "keyboard-activating");
797 priv->button_down = TRUE;
798 }
799}
800
801static void
802gtk_button_finish_activate (GtkButton *button,
803 gboolean do_it)
804{
805 GtkButtonPrivate *priv = gtk_button_get_instance_private (self: button);
806
807 gtk_widget_remove_css_class (GTK_WIDGET (button), css_class: "keyboard-activating");
808
809 g_source_remove (tag: priv->activate_timeout);
810 priv->activate_timeout = 0;
811
812 priv->button_down = FALSE;
813
814 if (do_it)
815 g_signal_emit (instance: button, signal_id: button_signals[CLICKED], detail: 0);
816}
817
818/**
819 * gtk_button_set_label: (attributes org.gtk.Method.set_property=label)
820 * @button: a `GtkButton`
821 * @label: a string
822 *
823 * Sets the text of the label of the button to @label.
824 *
825 * This will also clear any previously set labels.
826 */
827void
828gtk_button_set_label (GtkButton *button,
829 const char *label)
830{
831 GtkButtonPrivate *priv = gtk_button_get_instance_private (self: button);
832 GtkWidget *child;
833
834 g_return_if_fail (GTK_IS_BUTTON (button));
835
836 if (priv->child_type != LABEL_CHILD || priv->child == NULL)
837 {
838 child = gtk_label_new (NULL);
839 if (priv->use_underline)
840 {
841 gtk_label_set_use_underline (GTK_LABEL (child), setting: priv->use_underline);
842 gtk_label_set_mnemonic_widget (GTK_LABEL (child), GTK_WIDGET (button));
843 }
844 if (GTK_IS_CHECK_BUTTON (button))
845 gtk_label_set_xalign (GTK_LABEL (child), xalign: 0.0);
846
847 gtk_button_set_child (button, child);
848 }
849
850 gtk_label_set_label (GTK_LABEL (priv->child), str: label);
851 gtk_button_set_child_type (button, child_type: LABEL_CHILD);
852
853 gtk_accessible_update_property (self: GTK_ACCESSIBLE (ptr: button),
854 first_property: GTK_ACCESSIBLE_PROPERTY_LABEL, label,
855 -1);
856
857 g_object_notify_by_pspec (G_OBJECT (button), pspec: props[PROP_LABEL]);
858}
859
860/**
861 * gtk_button_get_label: (attributes org.gtk.Method.get_property=label)
862 * @button: a `GtkButton`
863 *
864 * Fetches the text from the label of the button.
865 *
866 * If the label text has not been set with [method@Gtk.Button.set_label]
867 * the return value will be %NULL. This will be the case if you create
868 * an empty button with [ctor@Gtk.Button.new] to use as a container.
869 *
870 * Returns: (nullable): The text of the label widget. This string is owned
871 * by the widget and must not be modified or freed.
872 */
873const char *
874gtk_button_get_label (GtkButton *button)
875{
876 GtkButtonPrivate *priv = gtk_button_get_instance_private (self: button);
877
878 g_return_val_if_fail (GTK_IS_BUTTON (button), NULL);
879
880 if (priv->child_type == LABEL_CHILD)
881 return gtk_label_get_label (GTK_LABEL (priv->child));
882
883 return NULL;
884}
885
886/**
887 * gtk_button_set_use_underline: (attributes org.gtk.Method.set_property=use-underline)
888 * @button: a `GtkButton`
889 * @use_underline: %TRUE if underlines in the text indicate mnemonics
890 *
891 * Sets whether to use underlines as mnemonics.
892 *
893 * If true, an underline in the text of the button label indicates
894 * the next character should be used for the mnemonic accelerator key.
895 */
896void
897gtk_button_set_use_underline (GtkButton *button,
898 gboolean use_underline)
899{
900 GtkButtonPrivate *priv = gtk_button_get_instance_private (self: button);
901
902 g_return_if_fail (GTK_IS_BUTTON (button));
903
904 use_underline = use_underline != FALSE;
905
906 if (use_underline != priv->use_underline)
907 {
908 if (priv->child_type == LABEL_CHILD)
909 {
910 gtk_label_set_use_underline (GTK_LABEL (priv->child), setting: use_underline);
911 gtk_label_set_mnemonic_widget (GTK_LABEL (priv->child), GTK_WIDGET (button));
912 }
913
914 priv->use_underline = use_underline;
915 g_object_notify_by_pspec (G_OBJECT (button), pspec: props[PROP_USE_UNDERLINE]);
916 }
917}
918
919/**
920 * gtk_button_get_use_underline: (attributes org.gtk.Method.get_property=use-underline)
921 * @button: a `GtkButton`
922 *
923 * gets whether underlines are interpreted as mnemonics.
924 *
925 * See [method@Gtk.Button.set_use_underline].
926 *
927 * Returns: %TRUE if an embedded underline in the button label
928 * indicates the mnemonic accelerator keys.
929 */
930gboolean
931gtk_button_get_use_underline (GtkButton *button)
932{
933 GtkButtonPrivate *priv = gtk_button_get_instance_private (self: button);
934
935 g_return_val_if_fail (GTK_IS_BUTTON (button), FALSE);
936
937 return priv->use_underline;
938}
939
940static void
941gtk_button_state_flags_changed (GtkWidget *widget,
942 GtkStateFlags previous_state)
943{
944 GtkButton *button = GTK_BUTTON (widget);
945
946 if (!gtk_widget_is_sensitive (widget))
947 gtk_button_do_release (button, FALSE);
948}
949
950/**
951 * gtk_button_set_icon_name: (attributes org.gtk.Method.set_property=icon-name)
952 * @button: A `GtkButton`
953 * @icon_name: An icon name
954 *
955 * Adds a `GtkImage` with the given icon name as a child.
956 *
957 * If @button already contains a child widget, that child widget will
958 * be removed and replaced with the image.
959 */
960void
961gtk_button_set_icon_name (GtkButton *button,
962 const char *icon_name)
963{
964 GtkButtonPrivate *priv = gtk_button_get_instance_private (self: button);
965
966 g_return_if_fail (GTK_IS_BUTTON (button));
967 g_return_if_fail (icon_name != NULL);
968
969 if (priv->child_type != ICON_CHILD || priv->child == NULL)
970 {
971 GtkWidget *child = g_object_new (GTK_TYPE_IMAGE,
972 first_property_name: "accessible-role", GTK_ACCESSIBLE_ROLE_PRESENTATION,
973 "icon-name", icon_name,
974 NULL);
975 gtk_button_set_child (GTK_BUTTON (button), child);
976 gtk_widget_set_valign (widget: child, align: GTK_ALIGN_CENTER);
977 }
978 else
979 {
980 gtk_image_set_from_icon_name (GTK_IMAGE (priv->child), icon_name);
981 }
982
983 gtk_accessible_update_relation (self: GTK_ACCESSIBLE (ptr: button),
984 first_relation: GTK_ACCESSIBLE_RELATION_LABELLED_BY, priv->child, NULL,
985 -1);
986
987 gtk_button_set_child_type (button, child_type: ICON_CHILD);
988 g_object_notify_by_pspec (G_OBJECT (button), pspec: props[PROP_ICON_NAME]);
989}
990
991/**
992 * gtk_button_get_icon_name: (attributes org.gtk.Method.get_property=icon-name)
993 * @button: A `GtkButton`
994 *
995 * Returns the icon name of the button.
996 *
997 * If the icon name has not been set with [method@Gtk.Button.set_icon_name]
998 * the return value will be %NULL. This will be the case if you create
999 * an empty button with [ctor@Gtk.Button.new] to use as a container.
1000 *
1001 * Returns: (nullable): The icon name set via [method@Gtk.Button.set_icon_name]
1002 */
1003const char *
1004gtk_button_get_icon_name (GtkButton *button)
1005{
1006 GtkButtonPrivate *priv = gtk_button_get_instance_private (self: button);
1007
1008 g_return_val_if_fail (GTK_IS_BUTTON (button), NULL);
1009
1010 if (priv->child_type == ICON_CHILD)
1011 return gtk_image_get_icon_name (GTK_IMAGE (priv->child));
1012
1013 return NULL;
1014}
1015
1016GtkGesture *
1017gtk_button_get_gesture (GtkButton *button)
1018{
1019 GtkButtonPrivate *priv = gtk_button_get_instance_private (self: button);
1020
1021 return priv->gesture;
1022}
1023
1024GtkActionHelper *
1025gtk_button_get_action_helper (GtkButton *button)
1026{
1027 GtkButtonPrivate *priv = gtk_button_get_instance_private (self: button);
1028
1029 return priv->action_helper;
1030}
1031
1032/**
1033 * gtk_button_set_child: (attributes org.gtk.Method.set_property=child)
1034 * @button: a `GtkButton`
1035 * @child: (nullable): the child widget
1036 *
1037 * Sets the child widget of @button.
1038 *
1039 * Note that by using this API, you take full responsibility for setting
1040 * up the proper accessibility label and description information for @button.
1041 * Most likely, you'll either set the accessibility label or description
1042 * for @button explicitly, or you'll set a labelled-by or described-by
1043 * relations from @child to @button.
1044 */
1045void
1046gtk_button_set_child (GtkButton *button,
1047 GtkWidget *child)
1048{
1049 GtkButtonPrivate *priv = gtk_button_get_instance_private (self: button);
1050
1051 g_return_if_fail (GTK_IS_BUTTON (button));
1052 g_return_if_fail (child == NULL || GTK_IS_WIDGET (child));
1053
1054 g_clear_pointer (&priv->child, gtk_widget_unparent);
1055
1056 priv->child = child;
1057
1058 if (priv->child)
1059 gtk_widget_set_parent (widget: priv->child, GTK_WIDGET (button));
1060
1061 gtk_button_set_child_type (button, child_type: WIDGET_CHILD);
1062 g_object_notify_by_pspec (G_OBJECT (button), pspec: props[PROP_CHILD]);
1063}
1064
1065/**
1066 * gtk_button_get_child: (attributes org.gtk.Method.get_property=child)
1067 * @button: a `GtkButton`
1068 *
1069 * Gets the child widget of @button.
1070 *
1071 * Returns: (nullable) (transfer none): the child widget of @button
1072 */
1073GtkWidget *
1074gtk_button_get_child (GtkButton *button)
1075{
1076 GtkButtonPrivate *priv = gtk_button_get_instance_private (self: button);
1077
1078 g_return_val_if_fail (GTK_IS_BUTTON (button), NULL);
1079
1080 return priv->child;
1081}
1082

source code of gtk/gtk/gtkbutton.c