[gtk+/buttons: 1/5] GtkButton: link to a GAction
- From: Ryan Lortie <ryanl src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+/buttons: 1/5] GtkButton: link to a GAction
- Date: Wed, 1 Dec 2010 07:52:12 +0000 (UTC)
commit da39db7b2fc7501b465c37b69c64a8876f151350
Author: Ryan Lortie <desrt desrt ca>
Date: Wed Dec 1 00:55:27 2010 -0500
GtkButton: link to a GAction
Link GtkButton to GAction and track the state of the action.
Make the 'active' state of GtkToggleButton decided from the state of the
GAction. Do no internal tracking of this state.
Bug #636108
gtk/gtkbutton.c | 193 +++++++++++++++++++++++++++++++++++++++++++++---
gtk/gtkbutton.h | 7 ++-
gtk/gtkbuttonprivate.h | 5 +
gtk/gtkcheckbutton.c | 8 ++-
gtk/gtkradiobutton.c | 8 ++-
gtk/gtktogglebutton.c | 26 ++++---
6 files changed, 222 insertions(+), 25 deletions(-)
---
diff --git a/gtk/gtkbutton.c b/gtk/gtkbutton.c
index 71fb865..5337887 100644
--- a/gtk/gtkbutton.c
+++ b/gtk/gtkbutton.c
@@ -90,6 +90,8 @@ enum {
PROP_XALIGN,
PROP_YALIGN,
PROP_IMAGE_POSITION,
+ PROP_IS_TOGGLE,
+ PROP_ACTION,
/* activatable properties */
PROP_ACTIVATABLE_RELATED_ACTION,
@@ -99,6 +101,7 @@ enum {
static void gtk_button_destroy (GtkWidget *widget);
static void gtk_button_dispose (GObject *object);
+static void gtk_button_finalize (GObject *object);
static void gtk_button_set_property (GObject *object,
guint prop_id,
const GValue *value,
@@ -188,6 +191,7 @@ gtk_button_class_init (GtkButtonClass *klass)
gobject_class->dispose = gtk_button_dispose;
gobject_class->set_property = gtk_button_set_property;
gobject_class->get_property = gtk_button_get_property;
+ gobject_class->finalize = gtk_button_finalize;
widget_class->get_preferred_width = gtk_button_get_preferred_width;
widget_class->get_preferred_height = gtk_button_get_preferred_height;
@@ -330,6 +334,36 @@ gtk_button_class_init (GtkButtonClass *klass)
GTK_POS_LEFT,
GTK_PARAM_READWRITE));
+ /**
+ * GtkButton:is-toggle:
+ *
+ * Determines if the default action added to the button has a boolean
+ * state or not. This property is only used at construct time.
+ *
+ * Since: 3.0
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_IS_TOGGLE,
+ g_param_spec_boolean ("is-toggle",
+ P_("Is toggle"),
+ P_("If the default action should be stateful"),
+ FALSE, GTK_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+
+ /**
+ * GtkButton:action:
+ *
+ * The #GAction that the button activates.
+ *
+ * Since: 3.0
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_ACTION,
+ g_param_spec_object ("action",
+ P_("Action"),
+ P_("The GAction that the button activates."),
+ G_TYPE_ACTION,
+ GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
g_object_class_override_property (gobject_class, PROP_ACTIVATABLE_RELATED_ACTION, "related-action");
g_object_class_override_property (gobject_class, PROP_ACTIVATABLE_USE_ACTION_APPEARANCE, "use-action-appearance");
@@ -578,6 +612,81 @@ gtk_button_destroy (GtkWidget *widget)
GTK_WIDGET_CLASS (gtk_button_parent_class)->destroy (widget);
}
+static void
+gtk_button_action_state_changed (GAction *action,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ GtkButton *button = GTK_BUTTON (user_data);
+ GVariant *state;
+
+ state = g_action_get_state (action);
+
+ if (!g_variant_equal (state, button->priv->state))
+ {
+ g_variant_unref (button->priv->state);
+ button->priv->state = g_variant_ref (state);
+
+ GTK_BUTTON_GET_CLASS (button)
+ ->action_state_changed (button, state);
+ }
+
+ g_variant_unref (state);
+}
+
+static void
+gtk_button_release_action (GtkButton *button)
+{
+ if (button->priv->state_id)
+ g_signal_handler_disconnect (button->priv->g_action,
+ button->priv->state_id);
+
+ g_object_unref (button->priv->g_action);
+ button->priv->state_id = 0;
+}
+
+static void
+gtk_button_setup_action (GtkButton *button)
+{
+ GVariant *state;
+
+ state = g_action_get_state (button->priv->g_action);
+
+ if (state != NULL)
+ button->priv->state_id =
+ g_signal_connect (button->priv->g_action, "notify::state",
+ G_CALLBACK (gtk_button_action_state_changed), button);
+
+ if (state != button->priv->state)
+ {
+ if (state == NULL ||
+ button->priv->state == NULL ||
+ !g_variant_equal (state, button->priv->state))
+ {
+ if (button->priv->state != NULL)
+ g_variant_unref (button->priv->state);
+
+ button->priv->state = NULL;
+
+ if (state != NULL)
+ button->priv->state = g_variant_ref (state);
+
+ GTK_BUTTON_GET_CLASS (button)
+ ->action_state_changed (button, state);
+ }
+ }
+
+ if (state)
+ g_variant_unref (state);
+}
+
+static void
+action_activated (GAction *action,
+ gpointer user_data)
+{
+ g_action_set_state (action, g_variant_new_boolean (!g_variant_get_boolean (g_action_get_state (action))));
+}
+
static GObject*
gtk_button_constructor (GType type,
guint n_construct_properties,
@@ -596,9 +705,31 @@ gtk_button_constructor (GType type,
priv->constructed = TRUE;
+ if (priv->g_action == NULL)
+ {
+ if (priv->is_toggle)
+ {
+ priv->g_action = G_ACTION (g_simple_action_new_stateful ("anonymous", NULL,
+ g_variant_new_boolean (FALSE)));
+ g_signal_connect (priv->g_action, "activate",
+ G_CALLBACK (action_activated), NULL);
+ }
+ else
+ priv->g_action = G_ACTION (g_simple_action_new ("anonymous", NULL));
+
+ gtk_button_setup_action (button);
+ }
+
if (priv->label_text != NULL)
gtk_button_construct_child (button);
-
+
+ /* This should be a default handler, but for compatibility reasons
+ * we need to support derived classes that don't chain up their
+ * clicked handler.
+ */
+ g_signal_connect_after (button, "clicked",
+ G_CALLBACK (gtk_real_button_clicked), NULL);
+
return object;
}
@@ -662,10 +793,22 @@ gtk_button_dispose (GObject *object)
gtk_activatable_do_set_related_action (GTK_ACTIVATABLE (button), NULL);
priv->action = NULL;
}
+
G_OBJECT_CLASS (gtk_button_parent_class)->dispose (object);
}
static void
+gtk_button_finalize (GObject *object)
+{
+ GtkButton *button = GTK_BUTTON (object);
+
+ gtk_button_release_action (button);
+
+ G_OBJECT_CLASS (gtk_button_parent_class)
+ ->finalize (object);
+}
+
+static void
gtk_button_set_property (GObject *object,
guint prop_id,
const GValue *value,
@@ -703,6 +846,13 @@ gtk_button_set_property (GObject *object,
case PROP_IMAGE_POSITION:
gtk_button_set_image_position (button, g_value_get_enum (value));
break;
+ case PROP_IS_TOGGLE:
+ priv->is_toggle = g_value_get_boolean (value);
+ break;
+ case PROP_ACTION:
+ if (g_value_get_object (value) != NULL)
+ gtk_button_set_action (button, g_value_get_object (value));
+ break;
case PROP_ACTIVATABLE_RELATED_ACTION:
gtk_button_set_related_action (button, g_value_get_object (value));
break;
@@ -753,6 +903,9 @@ gtk_button_get_property (GObject *object,
case PROP_IMAGE_POSITION:
g_value_set_enum (value, priv->image_position);
break;
+ case PROP_ACTION:
+ g_value_set_object (value, priv->g_action);
+ break;
case PROP_ACTIVATABLE_RELATED_ACTION:
g_value_set_object (value, priv->action);
break;
@@ -893,6 +1046,32 @@ gtk_button_sync_action_properties (GtkActivatable *activatable,
}
}
+GAction *
+gtk_button_get_action (GtkButton *button)
+{
+ g_return_val_if_fail (GTK_IS_BUTTON (button), NULL);
+
+ return button->priv->g_action;
+}
+
+void
+gtk_button_set_action (GtkButton *button,
+ GAction *action)
+{
+ g_return_if_fail (GTK_IS_BUTTON (button));
+ g_return_if_fail (G_IS_ACTION (action));
+
+ if (button->priv->g_action != action)
+ {
+ g_object_freeze_notify (G_OBJECT (button));
+ gtk_button_release_action (button);
+ button->priv->g_action = g_object_ref (action);
+ gtk_button_setup_action (button);
+ g_object_notify (G_OBJECT (button), "action");
+ g_object_thaw_notify (G_OBJECT (button));
+ }
+}
+
static void
gtk_button_set_related_action (GtkButton *button,
GtkAction *action)
@@ -902,15 +1081,6 @@ gtk_button_set_related_action (GtkButton *button,
if (priv->action == action)
return;
- /* This should be a default handler, but for compatibility reasons
- * we need to support derived classes that don't chain up their
- * clicked handler.
- */
- g_signal_handlers_disconnect_by_func (button, gtk_real_button_clicked, NULL);
- if (action)
- g_signal_connect_after (button, "clicked",
- G_CALLBACK (gtk_real_button_clicked), NULL);
-
gtk_activatable_do_set_related_action (GTK_ACTIVATABLE (button), action);
priv->action = action;
@@ -1807,6 +1977,9 @@ gtk_real_button_clicked (GtkButton *button)
if (priv->action)
gtk_action_activate (priv->action);
+
+ if (priv->g_action)
+ g_action_activate (priv->g_action, NULL);
}
static gboolean
diff --git a/gtk/gtkbutton.h b/gtk/gtkbutton.h
index 0bab7e6..3e2fee8 100644
--- a/gtk/gtkbutton.h
+++ b/gtk/gtkbutton.h
@@ -69,10 +69,11 @@ struct _GtkButtonClass
void (* activate) (GtkButton *button);
/* Padding for future expansion */
+ void (* action_state_changed) (GtkButton *button,
+ GVariant *state);
void (*_gtk_reserved1) (void);
void (*_gtk_reserved2) (void);
void (*_gtk_reserved3) (void);
- void (*_gtk_reserved4) (void);
};
@@ -120,6 +121,9 @@ void gtk_button_set_image_position (GtkButton *button,
GtkPositionType gtk_button_get_image_position (GtkButton *button);
GdkWindow* gtk_button_get_event_window (GtkButton *button);
+void gtk_button_set_action (GtkButton *button,
+ GAction *action);
+GAction * gtk_button_get_action (GtkButton *button);
void _gtk_button_set_depressed (GtkButton *button,
gboolean depressed);
@@ -132,6 +136,7 @@ void _gtk_button_paint (GtkButton *button,
const gchar *main_detail,
const gchar *default_detail);
+
G_END_DECLS
#endif /* __GTK_BUTTON_H__ */
diff --git a/gtk/gtkbuttonprivate.h b/gtk/gtkbuttonprivate.h
index b6007cd..da5f56b 100644
--- a/gtk/gtkbuttonprivate.h
+++ b/gtk/gtkbuttonprivate.h
@@ -24,6 +24,10 @@
struct _GtkButtonPrivate
{
GtkAction *action;
+ GAction *g_action;
+ guint state_id;
+ GVariant *state;
+
GtkPositionType image_position;
GtkWidget *image;
@@ -50,6 +54,7 @@ struct _GtkButtonPrivate
guint use_action_appearance : 1;
guint use_stock : 1;
guint use_underline : 1;
+ guint is_toggle : 1;
};
#endif /* __GTK_BUTTON_PRIVATE_H__ */
diff --git a/gtk/gtkcheckbutton.c b/gtk/gtkcheckbutton.c
index f07ecf1..f9087b5 100644
--- a/gtk/gtkcheckbutton.c
+++ b/gtk/gtkcheckbutton.c
@@ -101,14 +101,17 @@ gtk_check_button_init (GtkCheckButton *check_button)
GtkWidget*
gtk_check_button_new (void)
{
- return g_object_new (GTK_TYPE_CHECK_BUTTON, NULL);
+ return g_object_new (GTK_TYPE_CHECK_BUTTON, "is-toggle", TRUE, NULL);
}
GtkWidget*
gtk_check_button_new_with_label (const gchar *label)
{
- return g_object_new (GTK_TYPE_CHECK_BUTTON, "label", label, NULL);
+ return g_object_new (GTK_TYPE_CHECK_BUTTON,
+ "is-toggle", TRUE,
+ "label", label,
+ NULL);
}
/**
@@ -125,6 +128,7 @@ GtkWidget*
gtk_check_button_new_with_mnemonic (const gchar *label)
{
return g_object_new (GTK_TYPE_CHECK_BUTTON,
+ "is-toggle", TRUE,
"label", label,
"use-underline", TRUE,
NULL);
diff --git a/gtk/gtkradiobutton.c b/gtk/gtkradiobutton.c
index ff607ce..085f9ee 100644
--- a/gtk/gtkradiobutton.c
+++ b/gtk/gtkradiobutton.c
@@ -416,7 +416,7 @@ gtk_radio_button_new (GSList *group)
{
GtkRadioButton *radio_button;
- radio_button = g_object_new (GTK_TYPE_RADIO_BUTTON, NULL);
+ radio_button = g_object_new (GTK_TYPE_RADIO_BUTTON, "is-toggle", TRUE, NULL);
if (group)
gtk_radio_button_set_group (radio_button, group);
@@ -440,7 +440,10 @@ gtk_radio_button_new_with_label (GSList *group,
{
GtkWidget *radio_button;
- radio_button = g_object_new (GTK_TYPE_RADIO_BUTTON, "label", label, NULL) ;
+ radio_button = g_object_new (GTK_TYPE_RADIO_BUTTON,
+ "is-toggle", TRUE,
+ "label", label,
+ NULL);
if (group)
gtk_radio_button_set_group (GTK_RADIO_BUTTON (radio_button), group);
@@ -469,6 +472,7 @@ gtk_radio_button_new_with_mnemonic (GSList *group,
GtkWidget *radio_button;
radio_button = g_object_new (GTK_TYPE_RADIO_BUTTON,
+ "is-toggle", TRUE,
"label", label,
"use-underline", TRUE,
NULL);
diff --git a/gtk/gtktogglebutton.c b/gtk/gtktogglebutton.c
index a4979b3..eda7ab3 100644
--- a/gtk/gtktogglebutton.c
+++ b/gtk/gtktogglebutton.c
@@ -68,7 +68,6 @@ static gboolean gtk_toggle_button_mnemonic_activate (GtkWidget *widg
gboolean group_cycling);
static void gtk_toggle_button_pressed (GtkButton *button);
static void gtk_toggle_button_released (GtkButton *button);
-static void gtk_toggle_button_clicked (GtkButton *button);
static void gtk_toggle_button_set_property (GObject *object,
guint prop_id,
const GValue *value,
@@ -79,6 +78,9 @@ static void gtk_toggle_button_get_property (GObject *object,
GParamSpec *pspec);
static void gtk_toggle_button_update_state (GtkButton *button);
+static void gtk_toggle_button_action_state_changed (GtkButton *button,
+ GVariant *state);
+
static void gtk_toggle_button_activatable_interface_init (GtkActivatableIface *iface);
static void gtk_toggle_button_update (GtkActivatable *activatable,
@@ -113,7 +115,7 @@ gtk_toggle_button_class_init (GtkToggleButtonClass *class)
button_class->pressed = gtk_toggle_button_pressed;
button_class->released = gtk_toggle_button_released;
- button_class->clicked = gtk_toggle_button_clicked;
+ button_class->action_state_changed = gtk_toggle_button_action_state_changed;
button_class->enter = gtk_toggle_button_update_state;
button_class->leave = gtk_toggle_button_update_state;
@@ -165,7 +167,6 @@ gtk_toggle_button_init (GtkToggleButton *toggle_button)
GtkToggleButtonPrivate);
priv = toggle_button->priv;
- priv->active = FALSE;
priv->draw_indicator = FALSE;
GTK_BUTTON (toggle_button)->priv->depress_on_activate = TRUE;
}
@@ -220,13 +221,16 @@ gtk_toggle_button_sync_action_properties (GtkActivatable *activatable,
GtkWidget*
gtk_toggle_button_new (void)
{
- return g_object_new (GTK_TYPE_TOGGLE_BUTTON, NULL);
+ return g_object_new (GTK_TYPE_TOGGLE_BUTTON, "is-toggle", TRUE, NULL);
}
GtkWidget*
gtk_toggle_button_new_with_label (const gchar *label)
{
- return g_object_new (GTK_TYPE_TOGGLE_BUTTON, "label", label, NULL);
+ return g_object_new (GTK_TYPE_TOGGLE_BUTTON,
+ "is-toggle", TRUE,
+ "label", label,
+ NULL);
}
/**
@@ -243,6 +247,7 @@ GtkWidget*
gtk_toggle_button_new_with_mnemonic (const gchar *label)
{
return g_object_new (GTK_TYPE_TOGGLE_BUTTON,
+ "is-toggle", TRUE,
"label", label,
"use-underline", TRUE,
NULL);
@@ -377,6 +382,7 @@ void
_gtk_toggle_button_set_active (GtkToggleButton *toggle_button,
gboolean is_active)
{
+ g_warning ("this is broken...");
toggle_button->priv->active = is_active;
}
@@ -528,21 +534,21 @@ gtk_toggle_button_released (GtkButton *button)
}
static void
-gtk_toggle_button_clicked (GtkButton *button)
+gtk_toggle_button_action_state_changed (GtkButton *button,
+ GVariant *state)
{
GtkToggleButton *toggle_button = GTK_TOGGLE_BUTTON (button);
GtkToggleButtonPrivate *priv = toggle_button->priv;
- priv->active = !priv->active;
+ priv->active = state &&
+ g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN) &&
+ g_variant_get_boolean (state);
gtk_toggle_button_toggled (toggle_button);
gtk_toggle_button_update_state (button);
g_object_notify (G_OBJECT (toggle_button), "active");
-
- if (GTK_BUTTON_CLASS (gtk_toggle_button_parent_class)->clicked)
- GTK_BUTTON_CLASS (gtk_toggle_button_parent_class)->clicked (button);
}
static void
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]