diff --git a/gtk/gtkswitch.c b/gtk/gtkswitch.c index acb306db53..e67fce66ca 100644 --- a/gtk/gtkswitch.c +++ b/gtk/gtkswitch.c @@ -46,6 +46,8 @@ #include "gtktoggleaction.h" #include "gtkwidget.h" #include "gtkmarshalers.h" +#include "gtkapplicationprivate.h" +#include "gtkactionable.h" #include "a11y/gtkswitchaccessible.h" #include @@ -58,6 +60,10 @@ struct _GtkSwitchPrivate GdkWindow *event_window; GtkAction *action; + gchar *action_name; + GVariant *action_target; + GSimpleActionObserver *action_observer; + gint handle_x; gint offset; gint drag_start; @@ -76,7 +82,9 @@ enum PROP_ACTIVE, PROP_RELATED_ACTION, PROP_USE_ACTION_APPEARANCE, - LAST_PROP + LAST_PROP, + PROP_ACTION_NAME, + PROP_ACTION_TARGET }; enum @@ -89,9 +97,15 @@ static guint signals[LAST_SIGNAL] = { 0 }; static GParamSpec *switch_props[LAST_PROP] = { NULL, }; +static void gtk_switch_hierarchy_changed (GtkWidget *widget, + GtkWidget *previous_toplevel); + +static void gtk_switch_actionable_iface_init (GtkActionableInterface *iface); static void gtk_switch_activatable_interface_init (GtkActivatableIface *iface); G_DEFINE_TYPE_WITH_CODE (GtkSwitch, gtk_switch, GTK_TYPE_WIDGET, + G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIONABLE, + gtk_switch_actionable_iface_init) G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIVATABLE, gtk_switch_activatable_interface_init)); @@ -685,6 +699,124 @@ gtk_switch_set_use_action_appearance (GtkSwitch *sw, } } +static void +gtk_switch_update_action_observer (GtkSwitch *sw) +{ + GtkWidget *window; + + /* we are the only owner so this will clear all the signals */ + g_clear_object (&sw->priv->action_observer); + + window = gtk_widget_get_toplevel (GTK_WIDGET (sw)); + + if (GTK_IS_APPLICATION_WINDOW (window) && sw->priv->action_name) + { + GSimpleActionObserver *observer; + + observer = gtk_application_window_get_observer (GTK_APPLICATION_WINDOW (window), + sw->priv->action_name, + sw->priv->action_target); + + if (g_object_class_find_property (G_OBJECT_GET_CLASS (sw), "active")) + g_object_bind_property (observer, "active", sw, "active", G_BINDING_SYNC_CREATE); + g_object_bind_property (observer, "enabled", sw, "sensitive", G_BINDING_SYNC_CREATE); + + sw->priv->action_observer = observer; + } +} + +static void +gtk_switch_set_action_name (GtkActionable *actionable, + const gchar *action_name) +{ + GtkSwitch *sw = GTK_SWITCH (actionable); + + g_return_if_fail (GTK_IS_SWITCH (sw)); + + if (g_strcmp0 (action_name, sw->priv->action_name) != 0) + { + g_free (sw->priv->action_name); + sw->priv->action_name = g_strdup (action_name); + + gtk_switch_update_action_observer (sw); + + g_object_notify (G_OBJECT (sw), "action-name"); + } +} + +static void +gtk_switch_set_action_target_value (GtkActionable *actionable, + GVariant *action_target) +{ + GtkSwitch *sw = GTK_SWITCH (actionable); + + g_return_if_fail (GTK_IS_SWITCH (sw)); + + if (action_target != sw->priv->action_target && + (!action_target || !sw->priv->action_target || + !g_variant_equal (action_target, sw->priv->action_target))) + { + if (sw->priv->action_target) + g_variant_unref (sw->priv->action_target); + + sw->priv->action_target = NULL; + + if (action_target) + sw->priv->action_target = g_variant_ref_sink (action_target); + + gtk_switch_update_action_observer (sw); + + g_object_notify (G_OBJECT (sw), "action-target"); + } +} + +static const gchar * +gtk_switch_get_action_name (GtkActionable *actionable) +{ + GtkSwitch *sw = GTK_SWITCH (actionable); + + return sw->priv->action_name; +} + +static GVariant * +gtk_switch_get_action_target_value (GtkActionable *actionable) +{ + GtkSwitch *sw = GTK_SWITCH (actionable); + + return sw->priv->action_target; +} + +static void +gtk_switch_actionable_iface_init (GtkActionableInterface *iface) +{ + iface->get_action_name = gtk_switch_get_action_name; + iface->set_action_name = gtk_switch_set_action_name; + iface->get_action_target_value = gtk_switch_get_action_target_value; + iface->set_action_target_value = gtk_switch_set_action_target_value; +} + +static void +gtk_switch_hierarchy_changed (GtkWidget *widget, + GtkWidget *previous_toplevel) +{ + GtkSwitch *sw = GTK_SWITCH (widget); + GtkWidgetClass *parent_class; + + parent_class = GTK_WIDGET_CLASS (gtk_switch_parent_class); + if (parent_class->hierarchy_changed) + parent_class->hierarchy_changed (widget, previous_toplevel); + + if (sw->priv->action_name) + { + GtkWidget *toplevel; + + toplevel = gtk_widget_get_toplevel (widget); + + if (toplevel != previous_toplevel) + gtk_switch_update_action_observer (sw); + } +} + static void gtk_switch_set_property (GObject *gobject, guint prop_id, @@ -707,6 +839,14 @@ gtk_switch_set_property (GObject *gobject, gtk_switch_set_use_action_appearance (sw, g_value_get_boolean (value)); break; + case PROP_ACTION_NAME: + gtk_switch_set_action_name (GTK_ACTIONABLE (sw), g_value_get_string (value)); + break; + + case PROP_ACTION_TARGET: + gtk_switch_set_action_target_value (GTK_ACTIONABLE (sw), g_value_get_variant (value)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); } @@ -734,6 +874,14 @@ gtk_switch_get_property (GObject *gobject, g_value_set_boolean (value, priv->use_action_appearance); break; + case PROP_ACTION_NAME: + g_value_set_string (value, priv->action_name); + break; + + case PROP_ACTION_TARGET: + g_value_set_variant (value, priv->action_target); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); } @@ -744,6 +892,14 @@ gtk_switch_dispose (GObject *object) { GtkSwitchPrivate *priv = GTK_SWITCH (object)->priv; + g_clear_object (&priv->action_observer); + + if (priv->action_name) + { + g_free (priv->action_name); + priv->action_name = NULL; + } + if (priv->action) { gtk_activatable_do_set_related_action (GTK_ACTIVATABLE (object), NULL); @@ -804,6 +960,7 @@ gtk_switch_class_init (GtkSwitchClass *klass) widget_class->motion_notify_event = gtk_switch_motion; widget_class->enter_notify_event = gtk_switch_enter; widget_class->leave_notify_event = gtk_switch_leave; + widget_class->hierarchy_changed = gtk_switch_hierarchy_changed; klass->activate = gtk_switch_activate; @@ -839,6 +996,9 @@ gtk_switch_class_init (GtkSwitchClass *klass) G_TYPE_NONE, 0); widget_class->activate_signal = signals[ACTIVATE]; + g_object_class_override_property (gobject_class, PROP_ACTION_NAME, "action-name"); + g_object_class_override_property (gobject_class, PROP_ACTION_TARGET, "action-target"); + gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_SWITCH_ACCESSIBLE); } @@ -898,6 +1058,9 @@ gtk_switch_set_active (GtkSwitch *sw, g_object_notify_by_pspec (G_OBJECT (sw), switch_props[PROP_ACTIVE]); + if (priv->action_observer) + g_simple_action_observer_activate (priv->action_observer); + if (priv->action) gtk_action_activate (priv->action);