diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt index 77316a4fb6..c8e46d89a2 100644 --- a/docs/reference/gtk/gtk4-sections.txt +++ b/docs/reference/gtk/gtk4-sections.txt @@ -4366,6 +4366,9 @@ gtk_widget_class_add_binding_signal gtk_widget_class_add_binding_action gtk_widget_class_set_layout_manager_type gtk_widget_class_get_layout_manager_type +gtk_widget_class_set_activate_signal +gtk_widget_class_set_activate_signal_from_name +gtk_widget_class_get_activate_signal gtk_widget_activate gtk_widget_is_focus gtk_widget_grab_focus diff --git a/gtk/gtkbutton.c b/gtk/gtkbutton.c index c4038765a4..dc7955ff63 100644 --- a/gtk/gtkbutton.c +++ b/gtk/gtkbutton.c @@ -282,9 +282,10 @@ gtk_button_class_init (GtkButtonClass *klass) NULL, NULL, NULL, G_TYPE_NONE, 0); - widget_class->activate_signal = button_signals[ACTIVATE]; - activate_action = gtk_signal_action_new ("activate"); + gtk_widget_class_set_activate_signal (widget_class, button_signals[ACTIVATE]); + + activate_action = gtk_signal_action_new ("activate"); for (guint i = 0; i < G_N_ELEMENTS (activate_keyvals); i++) { GtkShortcut *activate_shortcut = gtk_shortcut_new (gtk_keyval_trigger_new (activate_keyvals[i], 0), diff --git a/gtk/gtkdialog.c b/gtk/gtkdialog.c index 11c1be5bc4..800119cf94 100644 --- a/gtk/gtkdialog.c +++ b/gtk/gtkdialog.c @@ -324,7 +324,7 @@ add_response_data (GtkDialog *dialog, if (GTK_IS_BUTTON (child)) signal_id = g_signal_lookup ("clicked", GTK_TYPE_BUTTON); else - signal_id = GTK_WIDGET_GET_CLASS (child)->activate_signal; + signal_id = gtk_widget_class_get_activate_signal (GTK_WIDGET_GET_CLASS (child)); if (signal_id) { @@ -1287,7 +1287,7 @@ gtk_dialog_buildable_custom_finished (GtkBuildable *buildable, if (GTK_IS_BUTTON (object)) signal_id = g_signal_lookup ("clicked", GTK_TYPE_BUTTON); else - signal_id = GTK_WIDGET_GET_CLASS (object)->activate_signal; + signal_id = gtk_widget_class_get_activate_signal (GTK_WIDGET_GET_CLASS (object)); if (signal_id && !is_action) { diff --git a/gtk/gtkexpander.c b/gtk/gtkexpander.c index 9b60db04a4..647ee68428 100644 --- a/gtk/gtkexpander.c +++ b/gtk/gtkexpander.c @@ -292,6 +292,7 @@ gtk_expander_class_init (GtkExpanderClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + guint activate_signal; gobject_class->dispose = gtk_expander_dispose; gobject_class->set_property = gtk_expander_set_property; @@ -368,7 +369,7 @@ gtk_expander_class_init (GtkExpanderClass *klass) GTK_TYPE_WIDGET, GTK_PARAM_READWRITE)); - widget_class->activate_signal = + activate_signal = g_signal_new (I_("activate"), G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, @@ -377,6 +378,7 @@ gtk_expander_class_init (GtkExpanderClass *klass) NULL, G_TYPE_NONE, 0); + gtk_widget_class_set_activate_signal (widget_class, activate_signal); gtk_widget_class_set_css_name (widget_class, I_("expander-widget")); gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_BUTTON); } diff --git a/gtk/gtkflowbox.c b/gtk/gtkflowbox.c index 7342369b6a..c2d92943f5 100644 --- a/gtk/gtkflowbox.c +++ b/gtk/gtkflowbox.c @@ -547,11 +547,11 @@ gtk_flow_box_child_class_init (GtkFlowBoxChildClass *class) NULL, NULL, NULL, G_TYPE_NONE, 0); - widget_class->activate_signal = child_signals[CHILD_ACTIVATE]; gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); gtk_widget_class_set_css_name (widget_class, I_("flowboxchild")); gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_GRID_CELL); + gtk_widget_class_set_activate_signal (widget_class, child_signals[CHILD_ACTIVATE]); } static void @@ -3837,7 +3837,7 @@ gtk_flow_box_class_init (GtkFlowBoxClass *class) NULL, G_TYPE_NONE, 0); - widget_class->activate_signal = signals[ACTIVATE_CURSOR_CHILD]; + gtk_widget_class_set_activate_signal (widget_class, signals[ACTIVATE_CURSOR_CHILD]); gtk_flow_box_add_move_binding (widget_class, GDK_KEY_Home, 0, GTK_MOVEMENT_BUFFER_ENDS, -1); diff --git a/gtk/gtkinfobar.c b/gtk/gtkinfobar.c index 6cfd14aaa7..608c6b77ff 100644 --- a/gtk/gtkinfobar.c +++ b/gtk/gtkinfobar.c @@ -551,7 +551,7 @@ gtk_info_bar_add_action_widget (GtkInfoBar *info_bar, if (GTK_IS_BUTTON (child)) signal_id = g_signal_lookup ("clicked", GTK_TYPE_BUTTON); else - signal_id = GTK_WIDGET_GET_CLASS (child)->activate_signal; + signal_id = gtk_widget_class_get_activate_signal (GTK_WIDGET_GET_CLASS (child)); if (signal_id) { @@ -1019,7 +1019,7 @@ gtk_info_bar_buildable_custom_finished (GtkBuildable *buildable, if (GTK_IS_BUTTON (object)) signal_id = g_signal_lookup ("clicked", GTK_TYPE_BUTTON); else - signal_id = GTK_WIDGET_GET_CLASS (object)->activate_signal; + signal_id = gtk_widget_class_get_activate_signal (GTK_WIDGET_GET_CLASS (object)); if (signal_id) { diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c index 58c7431814..7701422b62 100644 --- a/gtk/gtklabel.c +++ b/gtk/gtklabel.c @@ -1452,7 +1452,7 @@ gtk_label_mnemonic_activate (GtkWidget *widget, while (parent) { if (gtk_widget_get_can_focus (parent) || - (!group_cycling && GTK_WIDGET_GET_CLASS (parent)->activate_signal) || + (!group_cycling && gtk_widget_can_activate (parent)) || GTK_IS_NOTEBOOK (gtk_widget_get_parent (parent))) return gtk_widget_mnemonic_activate (parent, group_cycling); parent = gtk_widget_get_parent (parent); diff --git a/gtk/gtklistbox.c b/gtk/gtklistbox.c index 1c256c0537..72bc7c8539 100644 --- a/gtk/gtklistbox.c +++ b/gtk/gtklistbox.c @@ -637,7 +637,7 @@ gtk_list_box_class_init (GtkListBoxClass *klass) G_TYPE_FROM_CLASS (klass), _gtk_marshal_VOID__ENUM_INT_BOOLEAN_BOOLEANv); - widget_class->activate_signal = signals[ACTIVATE_CURSOR_ROW]; + gtk_widget_class_set_activate_signal (widget_class, signals[ACTIVATE_CURSOR_ROW]); gtk_list_box_add_move_binding (widget_class, GDK_KEY_Home, 0, GTK_MOVEMENT_BUFFER_ENDS, -1); @@ -3466,7 +3466,7 @@ gtk_list_box_row_class_init (GtkListBoxRowClass *klass) NULL, G_TYPE_NONE, 0); - widget_class->activate_signal = row_signals[ROW__ACTIVATE]; + gtk_widget_class_set_activate_signal (widget_class, row_signals[ROW__ACTIVATE]); /** * GtkListBoxRow:activatable: diff --git a/gtk/gtklistitemwidget.c b/gtk/gtklistitemwidget.c index ea601be4d5..abe6352688 100644 --- a/gtk/gtklistitemwidget.c +++ b/gtk/gtklistitemwidget.c @@ -263,7 +263,7 @@ gtk_list_item_widget_class_init (GtkListItemWidgetClass *klass) NULL, G_TYPE_NONE, 0); - widget_class->activate_signal = signals[ACTIVATE_SIGNAL]; + gtk_widget_class_set_activate_signal (widget_class, signals[ACTIVATE_SIGNAL]); /** * GtkListItem|listitem.select: diff --git a/gtk/gtkmodelbutton.c b/gtk/gtkmodelbutton.c index da9a3f5b52..6c6be9a123 100644 --- a/gtk/gtkmodelbutton.c +++ b/gtk/gtkmodelbutton.c @@ -1295,8 +1295,7 @@ gtk_model_button_class_init (GtkModelButtonClass *class) NULL, G_TYPE_NONE, 0); - widget_class->activate_signal = signals[SIGNAL_CLICKED]; - + gtk_widget_class_set_activate_signal (widget_class, signals[SIGNAL_CLICKED]); gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BOX_LAYOUT); gtk_widget_class_set_css_name (widget_class, I_("modelbutton")); gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_MENU_ITEM); diff --git a/gtk/gtkpopovermenubar.c b/gtk/gtkpopovermenubar.c index 447dc5fa65..455f21ef65 100644 --- a/gtk/gtkpopovermenubar.c +++ b/gtk/gtkpopovermenubar.c @@ -354,6 +354,7 @@ gtk_popover_menu_bar_item_class_init (GtkPopoverMenuBarItemClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + guint activate_signal; object_class->dispose = gtk_popover_menu_bar_item_dispose; object_class->finalize = gtk_popover_menu_bar_item_finalize; @@ -364,7 +365,7 @@ gtk_popover_menu_bar_item_class_init (GtkPopoverMenuBarItemClass *klass) klass->activate = gtk_popover_menu_bar_item_activate; - widget_class->activate_signal = + activate_signal = g_signal_new (I_("activate"), G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, @@ -375,6 +376,7 @@ gtk_popover_menu_bar_item_class_init (GtkPopoverMenuBarItemClass *klass) gtk_widget_class_set_css_name (widget_class, I_("item")); gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_MENU_ITEM); + gtk_widget_class_set_activate_signal (widget_class, activate_signal); } enum { diff --git a/gtk/gtkswitch.c b/gtk/gtkswitch.c index bb681d9123..cff20c9aa8 100644 --- a/gtk/gtkswitch.c +++ b/gtk/gtkswitch.c @@ -570,7 +570,8 @@ gtk_switch_class_init (GtkSwitchClass *klass) NULL, NULL, NULL, G_TYPE_NONE, 0); - widget_class->activate_signal = signals[ACTIVATE]; + + gtk_widget_class_set_activate_signal (widget_class, signals[ACTIVATE]); /** * GtkSwitch::state-set: diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index ab9de00cd2..b2636ffa58 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -885,7 +885,6 @@ gtk_widget_class_init (GtkWidgetClass *klass) gobject_class->set_property = gtk_widget_set_property; gobject_class->get_property = gtk_widget_get_property; - klass->activate_signal = 0; klass->show = gtk_widget_real_show; klass->hide = gtk_widget_real_hide; klass->map = gtk_widget_real_map; @@ -4296,11 +4295,31 @@ gtk_widget_mnemonic_activate (GtkWidget *widget, return handled; } +/*< private > + * gtk_widget_can_activate: + * @self: a #GtkWidget + * + * Checks whether a #GtkWidget can be activated using + * gtk_widget_activate(). + */ +gboolean +gtk_widget_can_activate (GtkWidget *self) +{ + g_return_val_if_fail (GTK_IS_WIDGET (self), FALSE); + + GtkWidgetClass *widget_class = GTK_WIDGET_GET_CLASS (self); + + if (widget_class->priv->activate_signal != 0) + return TRUE; + + return FALSE; +} + static gboolean gtk_widget_real_mnemonic_activate (GtkWidget *widget, gboolean group_cycling) { - if (!group_cycling && GTK_WIDGET_GET_CLASS (widget)->activate_signal) + if (!group_cycling && gtk_widget_can_activate (widget)) gtk_widget_activate (widget); else if (gtk_widget_get_can_focus (widget)) return gtk_widget_grab_focus (widget); @@ -4562,14 +4581,96 @@ gtk_widget_event (GtkWidget *widget, return return_val; } +/** + * gtk_widget_class_get_activate_signal: + * @widget_class: a #GtkWidgetClass + * + * Retrieves the signal id for the activation signal set using + * gtk_widget_class_set_activate_signal(). + * + * Returns: a signal id, or 0 if the widget class does not + * specify an activation signal + */ +guint +gtk_widget_class_get_activate_signal (GtkWidgetClass *widget_class) +{ + g_return_val_if_fail (GTK_IS_WIDGET_CLASS (widget_class), 0); + + return widget_class->priv->activate_signal; +} + +/** + * gtk_widget_class_set_activate_signal: + * @widget_class: a #GtkWidgetClass + * @signal_id: the id for the activate signal + * + * Sets the #GtkWidgetClass.activate_signal field with the + * given @signal_id; the signal will be emitted when calling + * gtk_widget_activate(). + * + * The @signal_id must have been registered with g_signal_new() + * or g_signal_newv() before calling this function. + */ +void +gtk_widget_class_set_activate_signal (GtkWidgetClass *widget_class, + guint signal_id) +{ + g_return_if_fail (GTK_IS_WIDGET_CLASS (widget_class)); + g_return_if_fail (signal_id != 0); + + widget_class->priv->activate_signal = signal_id; +} + +/** + * gtk_widget_class_set_activate_signal_from_name: + * @widget_class: a #GtkWidgetClass + * @signal_name: the name of the activate signal of @widget_type + * + * Sets the #GtkWidgetClass.activate_signal field with the signal id for + * the given @signal_name; the signal will be emitted when calling + * gtk_widget_activate(). + * + * The @signal_name of @widget_type must have been registered with + * g_signal_new() or g_signal_newv() before calling this function. + */ +void +gtk_widget_class_set_activate_signal_from_name (GtkWidgetClass *widget_class, + const char *signal_name) +{ + guint signal_id; + + g_return_if_fail (GTK_IS_WIDGET_CLASS (widget_class)); + g_return_if_fail (signal_name != NULL); + + signal_id = g_signal_lookup (signal_name, G_TYPE_FROM_CLASS (widget_class)); + if (signal_id == 0) + { + g_critical ("Widget type “%s” does not have a “%s” signal", + G_OBJECT_CLASS_NAME (widget_class), + signal_name); + return; + } + + widget_class->priv->activate_signal = signal_id; +} + /** * gtk_widget_activate: * @widget: a #GtkWidget that’s activatable * * For widgets that can be “activated” (buttons, menu items, etc.) - * this function activates them. Activation is what happens when you - * press Enter on a widget during key navigation. If @widget isn't - * activatable, the function returns %FALSE. + * this function activates them. The activation will emit the signal + * set using gtk_widget_class_set_activate_signal() during class + * initialization. + * + * Activation is what happens when you press Enter on a widget during + * key navigation. + * + * If you wish to handle the activation keybinding yourself, it is + * recommended to use gtk_widget_class_add_shortcut() with an action + * created with gtk_signal_action_new(). + * + * If @widget isn't activatable, the function returns %FALSE. * * Returns: %TRUE if the widget was activatable **/ @@ -4578,10 +4679,11 @@ gtk_widget_activate (GtkWidget *widget) { g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); - if (GTK_WIDGET_GET_CLASS (widget)->activate_signal) + if (gtk_widget_can_activate (widget)) { + GtkWidgetClass *widget_class = GTK_WIDGET_GET_CLASS (widget); /* FIXME: we should eventually check the signals signature here */ - g_signal_emit (widget, GTK_WIDGET_GET_CLASS (widget)->activate_signal, 0); + g_signal_emit (widget, widget_class->priv->activate_signal, 0); return TRUE; } diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h index 61379b558e..765cb207c3 100644 --- a/gtk/gtkwidget.h +++ b/gtk/gtkwidget.h @@ -120,9 +120,6 @@ struct _GtkWidget * element in the widget class structure in order for the class mechanism * to work correctly. This allows a GtkWidgetClass pointer to be cast to * a GObjectClass pointer. - * @activate_signal: The signal to emit when a widget of this class is - * activated, gtk_widget_activate() handles the emission. - * Implementation of this signal is optional. * @show: Signal emitted when widget is shown * @hide: Signal emitted when widget is hidden. * @map: Signal emitted when widget is going to be mapped, that is @@ -194,8 +191,6 @@ struct _GtkWidgetClass /*< public >*/ - guint activate_signal; - /* basics */ void (* show) (GtkWidget *widget); void (* hide) (GtkWidget *widget); @@ -364,6 +359,15 @@ GDK_AVAILABLE_IN_ALL void gtk_widget_class_add_shortcut (GtkWidgetClass *widget_class, GtkShortcut *shortcut); +GDK_AVAILABLE_IN_ALL +void gtk_widget_class_set_activate_signal (GtkWidgetClass *widget_class, + guint signal_id); +GDK_AVAILABLE_IN_ALL +void gtk_widget_class_set_activate_signal_from_name (GtkWidgetClass *widget_class, + const char *signal_name); +GDK_AVAILABLE_IN_ALL +guint gtk_widget_class_get_activate_signal (GtkWidgetClass *widget_class); + GDK_AVAILABLE_IN_ALL gboolean gtk_widget_mnemonic_activate (GtkWidget *widget, gboolean group_cycling); diff --git a/gtk/gtkwidgetprivate.h b/gtk/gtkwidgetprivate.h index a79ee938da..c024c6adf1 100644 --- a/gtk/gtkwidgetprivate.h +++ b/gtk/gtkwidgetprivate.h @@ -215,6 +215,7 @@ struct _GtkWidgetClassPrivate GType layout_manager_type; GtkWidgetAction *actions; GtkAccessibleRole accessible_role; + guint activate_signal; }; void gtk_widget_root (GtkWidget *widget); @@ -356,6 +357,8 @@ guint gtk_widget_add_surface_transform_changed_callback (GtkWidget void gtk_widget_remove_surface_transform_changed_callback (GtkWidget *widget, guint id); +gboolean gtk_widget_can_activate (GtkWidget *widget); + /* focus vfuncs for non-focusable containers with focusable children */ gboolean gtk_widget_grab_focus_child (GtkWidget *widget); gboolean gtk_widget_focus_child (GtkWidget *widget,