diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt index d02d884f1b..d46a2d39a5 100644 --- a/docs/reference/gtk/gtk4-sections.txt +++ b/docs/reference/gtk/gtk4-sections.txt @@ -4035,6 +4035,7 @@ gtk_widget_remove_tick_callback gtk_widget_size_allocate gtk_widget_allocate gtk_widget_class_add_shortcut +gtk_widget_class_add_binding gtk_widget_class_add_binding_signal gtk_widget_add_accelerator gtk_widget_remove_accelerator @@ -6096,6 +6097,8 @@ gtk_shortcut_get_arguments gtk_shortcut_set_arguments gtk_shortcut_get_signal gtk_shortcut_set_signal +gtk_shortcut_has_callback +gtk_shortcut_set_callback GTK_TYPE_SHORTCUT diff --git a/gtk/gtkshortcut.c b/gtk/gtkshortcut.c index 97977e3c92..c304c808fa 100644 --- a/gtk/gtkshortcut.c +++ b/gtk/gtkshortcut.c @@ -56,6 +56,9 @@ struct _GtkShortcut GtkShortcutTrigger *trigger; char *signal; + GtkShortcutFunc callback; + gpointer user_data; + GDestroyNotify destroy_notify; GVariant *args; }; @@ -63,6 +66,7 @@ enum { PROP_0, PROP_ARGUMENTS, + PROP_CALLBACK, PROP_SIGNAL, PROP_TRIGGER, @@ -81,6 +85,15 @@ gtk_shortcut_dispose (GObject *object) g_clear_pointer (&self->trigger, gtk_shortcut_trigger_unref); g_clear_pointer (&self->signal, g_free); g_clear_pointer (&self->args, g_variant_unref); + if (self->callback) + { + if (self->destroy_notify) + self->destroy_notify (self->user_data); + + self->callback = NULL; + self->user_data = NULL; + self->destroy_notify = NULL; + } G_OBJECT_CLASS (gtk_shortcut_parent_class)->dispose (object); } @@ -99,6 +112,10 @@ gtk_shortcut_get_property (GObject *object, g_value_set_variant (value, self->args); break; + case PROP_CALLBACK: + g_value_set_boolean (value, self->callback != NULL); + break; + case PROP_SIGNAL: g_value_set_string (value, self->signal); break; @@ -163,6 +180,18 @@ gtk_shortcut_class_init (GtkShortcutClass *klass) NULL, G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + /** + * GtkShortcut:callback: + * + * Whether a callback is used for shortcut activation + */ + properties[PROP_CALLBACK] = + g_param_spec_boolean ("callback", + P_("Callback"), + P_("Whether a callback is used for shortcut activation"), + FALSE, + G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + /** * GtkShortcut:signal: * @@ -223,7 +252,11 @@ gtk_shortcut_activate (GtkShortcut *self, g_return_val_if_fail (GTK_IS_SHORTCUT (self), FALSE); g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); - if (self->signal) + if (self->callback) + { + return self->callback (widget, self->args, self->user_data); + } + else if (self->signal) { GError *error = NULL; gboolean handled; @@ -320,6 +353,28 @@ gtk_shortcut_set_arguments (GtkShortcut *self, g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ARGUMENTS]); } +static void +gtk_shortcut_clear_activation (GtkShortcut *self) +{ + if (self->signal) + { + g_clear_pointer (&self->signal, g_free); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SIGNAL]); + } + + if (self->callback) + { + if (self->destroy_notify) + self->destroy_notify (self->user_data); + + self->callback = NULL; + self->user_data = NULL; + self->destroy_notify = NULL; + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CALLBACK]); + } +} + const char * gtk_shortcut_get_signal (GtkShortcut *self) { @@ -337,9 +392,42 @@ gtk_shortcut_set_signal (GtkShortcut *self, if (g_strcmp0 (self->signal, signal) == 0) return; - g_clear_pointer (&self->signal, g_free); + g_object_freeze_notify (G_OBJECT (self)); + + gtk_shortcut_clear_activation (self); self->signal = g_strdup (signal); g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SIGNAL]); + + g_object_thaw_notify (G_OBJECT (self)); +} + +gboolean +gtk_shortcut_has_callback (GtkShortcut *self) +{ + g_return_val_if_fail (GTK_IS_SHORTCUT (self), FALSE); + + return self->callback != NULL; +} + +void +gtk_shortcut_set_callback (GtkShortcut *self, + GtkShortcutFunc callback, + gpointer data, + GDestroyNotify destroy) +{ + g_return_if_fail (GTK_IS_SHORTCUT (self)); + + g_object_freeze_notify (G_OBJECT (self)); + + gtk_shortcut_clear_activation (self); + + self->callback = callback; + self->user_data = data; + self->destroy_notify = destroy; + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CALLBACK]); + + g_object_thaw_notify (G_OBJECT (self)); } diff --git a/gtk/gtkshortcut.h b/gtk/gtkshortcut.h index 5920e6505f..76c8af4d0c 100644 --- a/gtk/gtkshortcut.h +++ b/gtk/gtkshortcut.h @@ -24,6 +24,10 @@ G_BEGIN_DECLS +typedef gboolean (* GtkShortcutFunc) (GtkWidget *widget, + GVariant *args, + gpointer user_data); + #define GTK_TYPE_SHORTCUT (gtk_shortcut_get_type ()) GDK_AVAILABLE_IN_ALL @@ -56,6 +60,13 @@ const char * gtk_shortcut_get_signal (GtkShortcut GDK_AVAILABLE_IN_ALL void gtk_shortcut_set_signal (GtkShortcut *self, const gchar *signal); +GDK_AVAILABLE_IN_ALL +gboolean gtk_shortcut_has_callback (GtkShortcut *self); +GDK_AVAILABLE_IN_ALL +void gtk_shortcut_set_callback (GtkShortcut *self, + GtkShortcutFunc callback, + gpointer data, + GDestroyNotify destroy); G_END_DECLS diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index 4b90ab5540..f6513b3b97 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -4318,6 +4318,56 @@ gtk_widget_real_size_allocate (GtkWidget *widget, { } +/** + * gtk_widget_class_add_binding: (skip) + * @widget_class: the class to add the binding to + * @keyval: key value of binding to install + * @mods: key modifier of binding to install + * @callback: the callback to call upon activation + * @format_string: GVariant format string for arguments or %NULL for + * no arguments + * @...: arguments, as given by format string. + * + * Creates a new shortcut for @widget_class that calls the given @callback + * with arguments read according to @format_string. + * The arguments and format string must be provided in the same way as + * with g_variant_new(). + * + * This function is a convenience wrapper around + * gtk_widget_class_add_shortcut() and must be called during class + * initialization. It does not provide for user_data, if you need that, + * you will have to use gtk_widget_class_add_shortcut() with a custom + * shortcut. + **/ +void +gtk_widget_class_add_binding (GtkWidgetClass *widget_class, + guint keyval, + GdkModifierType mods, + GtkShortcutFunc func, + const gchar *format_string, + ...) +{ + GtkShortcut *shortcut; + + g_return_if_fail (GTK_IS_WIDGET_CLASS (widget_class)); + + shortcut = gtk_shortcut_new (); + gtk_shortcut_set_trigger (shortcut, gtk_keyval_trigger_new (keyval, mods)); + gtk_shortcut_set_callback (shortcut, func, NULL, NULL); + if (format_string) + { + va_list args; + va_start (args, format_string); + gtk_shortcut_set_arguments (shortcut, + g_variant_new_va (format_string, NULL, &args)); + va_end (args); + } + + gtk_widget_class_add_shortcut (widget_class, shortcut); + + g_object_unref (shortcut); +} + /** * gtk_widget_class_add_binding_signal: (skip) * @widget_class: the class to add the binding to diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h index 6513fc311f..40af2c6a18 100644 --- a/gtk/gtkwidget.h +++ b/gtk/gtkwidget.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -373,6 +374,13 @@ void gtk_widget_class_set_layout_manager_type (GtkWidg GDK_AVAILABLE_IN_ALL GType gtk_widget_class_get_layout_manager_type (GtkWidgetClass *widget_class); +GDK_AVAILABLE_IN_ALL +void gtk_widget_class_add_binding (GtkWidgetClass *widget_class, + guint keyval, + GdkModifierType mods, + GtkShortcutFunc callback, + const gchar *format_string, + ...); GDK_AVAILABLE_IN_ALL void gtk_widget_class_add_binding_signal (GtkWidgetClass *widget_class,