[gnome-builder] egg: add EggRadioBox
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder] egg: add EggRadioBox
- Date: Wed, 29 Jun 2016 09:49:17 +0000 (UTC)
commit 5585181d816bcbf6761d8f1517ecd82bff9d7c01
Author: Christian Hergert <chergert redhat com>
Date: Wed Jun 29 02:37:08 2016 -0700
egg: add EggRadioBox
This is a helper widget to replace GtkComboBox with a simple set of
radio buttons (possibly in multiple rows). See various new Builder
designs in bug 767355.
contrib/egg/Makefile.am | 2 +
contrib/egg/egg-radio-box.c | 480 +++++++++++++++++++++++++++++++++++++++++++
contrib/egg/egg-radio-box.h | 50 +++++
tests/Makefile.am | 6 +
tests/test-egg-radio-box.c | 34 +++
5 files changed, 572 insertions(+), 0 deletions(-)
---
diff --git a/contrib/egg/Makefile.am b/contrib/egg/Makefile.am
index 7d5b2d8..a34d6d0 100644
--- a/contrib/egg/Makefile.am
+++ b/contrib/egg/Makefile.am
@@ -24,6 +24,7 @@ headers_DATA = \
egg-pill-box.h \
egg-priority-box.h \
egg-private.h \
+ egg-radio-box.h \
egg-search-bar.h \
egg-settings-flag-action.h \
egg-settings-sandwich.h \
@@ -55,6 +56,7 @@ libegg_private_la_SOURCES = \
egg-menu-manager.c \
egg-pill-box.c \
egg-priority-box.c \
+ egg-radio-box.c \
egg-search-bar.c \
egg-settings-flag-action.c \
egg-settings-sandwich.c \
diff --git a/contrib/egg/egg-radio-box.c b/contrib/egg/egg-radio-box.c
new file mode 100644
index 0000000..8b552c6
--- /dev/null
+++ b/contrib/egg/egg-radio-box.c
@@ -0,0 +1,480 @@
+/* egg-radio-box.c
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "egg-radio-box"
+
+#include "egg-radio-box.h"
+
+/*
+ * XXX: Ideally we would manage all the size requests ourselves. However,
+ * that takes some more work to do correctly (and support stuff like
+ * linked, etc).
+ */
+#define N_PER_ROW 4
+
+typedef struct
+{
+ gchar *id;
+ gchar *text;
+ GtkToggleButton *button;
+} EggRadioBoxItem;
+
+typedef struct
+{
+ GArray *items;
+ GSimpleAction *active_action;
+
+ GtkBox *vbox;
+ GtkBox *hbox;
+
+ guint n_in_hbox;
+ guint has_more : 1;
+ guint show_more : 1;
+} EggRadioBoxPrivate;
+
+static void buildable_iface_init (GtkBuildableIface *iface);
+
+G_DEFINE_TYPE_EXTENDED (EggRadioBox, egg_radio_box, GTK_TYPE_BIN, 0,
+ G_ADD_PRIVATE (EggRadioBox)
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, buildable_iface_init))
+
+enum {
+ PROP_0,
+ PROP_ACTIVE_ID,
+ PROP_HAS_MORE,
+ PROP_SHOW_MORE,
+ N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+static gboolean
+egg_radio_box_get_has_more (EggRadioBox *self)
+{
+ EggRadioBoxPrivate *priv = egg_radio_box_get_instance_private (self);
+
+ g_return_val_if_fail (EGG_IS_RADIO_BOX (self), FALSE);
+
+ return priv->has_more;
+}
+
+static gboolean
+egg_radio_box_get_show_more (EggRadioBox *self)
+{
+ EggRadioBoxPrivate *priv = egg_radio_box_get_instance_private (self);
+
+ g_return_val_if_fail (EGG_IS_RADIO_BOX (self), FALSE);
+
+ return priv->show_more;
+}
+
+static void
+show_first_item (GtkWidget *widget,
+ gpointer user_data)
+{
+ gboolean *toggled = user_data;
+
+ if (*toggled == TRUE)
+ gtk_widget_hide (widget);
+ else
+ *toggled = TRUE;
+}
+
+static void
+egg_radio_box_set_show_more (EggRadioBox *self,
+ gboolean show_more)
+{
+ EggRadioBoxPrivate *priv = egg_radio_box_get_instance_private (self);
+ gboolean toggled = FALSE;
+
+ g_return_if_fail (EGG_IS_RADIO_BOX (self));
+
+ if (show_more)
+ gtk_widget_show_all (GTK_WIDGET (priv->vbox));
+ else
+ gtk_container_foreach (GTK_CONTAINER (priv->vbox),
+ show_first_item,
+ &toggled);
+
+ priv->show_more = !!show_more;
+}
+
+static void
+egg_radio_box_item_clear (EggRadioBoxItem *item)
+{
+ g_free (item->id);
+ g_free (item->text);
+}
+
+static void
+egg_radio_box_active_action (GSimpleAction *action,
+ GVariant *variant,
+ gpointer user_data)
+{
+ EggRadioBox *self = user_data;
+
+ g_assert (G_IS_SIMPLE_ACTION (action));
+ g_assert (variant != NULL);
+ g_assert (g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING));
+ g_assert (EGG_IS_RADIO_BOX (self));
+
+ egg_radio_box_set_active_id (self, g_variant_get_string (variant, NULL));
+}
+
+static void
+egg_radio_box_finalize (GObject *object)
+{
+ EggRadioBox *self = (EggRadioBox *)object;
+ EggRadioBoxPrivate *priv = egg_radio_box_get_instance_private (self);
+
+ g_clear_pointer (&priv->items, g_array_unref);
+ g_clear_object (&priv->active_action);
+
+ G_OBJECT_CLASS (egg_radio_box_parent_class)->finalize (object);
+}
+
+static void
+egg_radio_box_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EggRadioBox *self = EGG_RADIO_BOX (object);
+
+ switch (prop_id)
+ {
+ case PROP_ACTIVE_ID:
+ g_value_take_string (value, egg_radio_box_get_active_id (self));
+ break;
+
+ case PROP_HAS_MORE:
+ g_value_set_boolean (value, egg_radio_box_get_has_more (self));
+ break;
+
+ case PROP_SHOW_MORE:
+ g_value_set_boolean (value, egg_radio_box_get_show_more (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+egg_radio_box_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EggRadioBox *self = EGG_RADIO_BOX (object);
+
+ switch (prop_id)
+ {
+ case PROP_ACTIVE_ID:
+ egg_radio_box_set_active_id (self, g_value_get_string (value));
+ break;
+
+ case PROP_SHOW_MORE:
+ egg_radio_box_set_show_more (self, g_value_get_boolean (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+egg_radio_box_class_init (EggRadioBoxClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->finalize = egg_radio_box_finalize;
+ object_class->get_property = egg_radio_box_get_property;
+ object_class->set_property = egg_radio_box_set_property;
+
+ properties [PROP_ACTIVE_ID] =
+ g_param_spec_string ("active-id",
+ "Active Id",
+ "Active Id",
+ NULL,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_HAS_MORE] =
+ g_param_spec_boolean ("has-more",
+ "Has More",
+ "Has more items to view",
+ FALSE,
+ (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_SHOW_MORE] =
+ g_param_spec_boolean ("show-more",
+ "Show More",
+ "Show additional items",
+ FALSE,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+
+ gtk_widget_class_set_css_name (widget_class, "radiobox");
+}
+
+static void
+egg_radio_box_init (EggRadioBox *self)
+{
+ EggRadioBoxPrivate *priv = egg_radio_box_get_instance_private (self);
+ g_autoptr(GSimpleActionGroup) group = g_simple_action_group_new ();
+
+ priv->items = g_array_new (FALSE, FALSE, sizeof (EggRadioBoxItem));
+ g_array_set_clear_func (priv->items, (GDestroyNotify)egg_radio_box_item_clear);
+
+ priv->vbox = g_object_new (GTK_TYPE_BOX,
+ "orientation", GTK_ORIENTATION_VERTICAL,
+ "spacing", 12,
+ "visible", TRUE,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (priv->vbox));
+
+ priv->active_action = g_simple_action_new_stateful ("active",
+ G_VARIANT_TYPE_STRING,
+ g_variant_new_string (""));
+ g_signal_connect (priv->active_action,
+ "change-state",
+ G_CALLBACK (egg_radio_box_active_action),
+ self);
+ g_action_map_add_action (G_ACTION_MAP (group), G_ACTION (priv->active_action));
+ gtk_widget_insert_action_group (GTK_WIDGET (self), "radiobox", G_ACTION_GROUP (group));
+}
+
+void
+egg_radio_box_add_item (EggRadioBox *self,
+ const gchar *id,
+ const gchar *text)
+{
+ EggRadioBoxPrivate *priv = egg_radio_box_get_instance_private (self);
+ EggRadioBoxItem item = { 0 };
+
+ g_return_if_fail (EGG_IS_RADIO_BOX (self));
+ g_return_if_fail (id != NULL);
+ g_return_if_fail (text != NULL);
+
+ item.id = g_strdup (id);
+ item.text = g_strdup (text);
+ item.button = g_object_new (GTK_TYPE_TOGGLE_BUTTON,
+ "action-name", "radiobox.active",
+ "action-target", g_variant_new_string (id),
+ "label", text,
+ "visible", TRUE,
+ NULL);
+
+ g_array_append_val (priv->items, item);
+
+ if (priv->n_in_hbox % N_PER_ROW == 0)
+ {
+ priv->n_in_hbox = 0;
+ priv->has_more = priv->hbox != NULL;
+ priv->hbox = g_object_new (GTK_TYPE_BOX,
+ "orientation", GTK_ORIENTATION_HORIZONTAL,
+ "visible", !priv->has_more || priv->show_more,
+ NULL);
+ gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (priv->hbox)), "linked");
+ gtk_container_add (GTK_CONTAINER (priv->vbox), GTK_WIDGET (priv->hbox));
+ }
+
+ gtk_container_add_with_properties (GTK_CONTAINER (priv->hbox), GTK_WIDGET (item.button),
+ "expand", TRUE,
+ NULL);
+
+ priv->n_in_hbox++;
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_HAS_MORE]);
+}
+
+void
+egg_radio_box_set_active_id (EggRadioBox *self,
+ const gchar *id)
+{
+ EggRadioBoxPrivate *priv = egg_radio_box_get_instance_private (self);
+
+ g_return_if_fail (EGG_IS_RADIO_BOX (self));
+ g_return_if_fail (id != NULL);
+
+ g_simple_action_set_state (priv->active_action, g_variant_new_string (id));
+}
+
+gchar *
+egg_radio_box_get_active_id (EggRadioBox *self)
+{
+ EggRadioBoxPrivate *priv = egg_radio_box_get_instance_private (self);
+ g_autoptr(GVariant) state = NULL;
+
+ g_return_val_if_fail (EGG_IS_RADIO_BOX (self), NULL);
+
+ state = g_action_get_state (G_ACTION (priv->active_action));
+
+ if (state != NULL)
+ return g_variant_dup_string (state, NULL);
+
+ return NULL;
+}
+
+GtkWidget *
+egg_radio_box_new (void)
+{
+ return g_object_new (EGG_TYPE_RADIO_BOX, NULL);
+}
+
+typedef struct
+{
+ EggRadioBox *self;
+ gchar *id;
+ GString *text;
+} ItemParserData;
+
+static void
+item_parser_start_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer user_data,
+ GError **error)
+{
+ ItemParserData *parser_data = user_data;
+
+ g_assert (context != NULL);
+ g_assert (element_name != NULL);
+ g_assert (parser_data != NULL);
+
+ if (g_strcmp0 (element_name, "item") == 0)
+ {
+ const gchar *translatable = NULL;
+
+ g_clear_pointer (&parser_data->id, g_free);
+ g_string_truncate (parser_data->text, 0);
+
+ if (!g_markup_collect_attributes (element_name, attribute_names, attribute_values, error,
+ G_MARKUP_COLLECT_STRDUP, "id", &parser_data->id,
+ G_MARKUP_COLLECT_STRING, "translatable", &translatable,
+ G_MARKUP_COLLECT_INVALID))
+ return;
+ }
+}
+
+static void
+item_parser_end_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error)
+{
+ ItemParserData *parser_data = user_data;
+
+ g_assert (context != NULL);
+ g_assert (element_name != NULL);
+ g_assert (parser_data != NULL);
+
+ if (g_strcmp0 (element_name, "item") == 0)
+ {
+ if (parser_data->id && parser_data->text != NULL)
+ egg_radio_box_add_item (parser_data->self, parser_data->id, parser_data->text->str);
+ }
+}
+
+static void
+item_parser_text (GMarkupParseContext *context,
+ const gchar *text,
+ gsize text_len,
+ gpointer user_data,
+ GError **error)
+{
+ ItemParserData *parser_data = user_data;
+
+ g_assert (parser_data != NULL);
+
+ if (parser_data->text == NULL)
+ parser_data->text = g_string_new (NULL);
+
+ g_string_append_len (parser_data->text, text, text_len);
+}
+
+static GMarkupParser ItemParser = {
+ item_parser_start_element,
+ item_parser_end_element,
+ item_parser_text,
+};
+
+static gboolean
+egg_radio_box_custom_tag_start (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ GObject *child,
+ const gchar *tagname,
+ GMarkupParser *parser,
+ gpointer *data)
+{
+ EggRadioBox *self = (EggRadioBox *)buildable;
+
+ g_assert (EGG_IS_RADIO_BOX (self));
+ g_assert (GTK_IS_BUILDER (builder));
+ g_assert (tagname != NULL);
+ g_assert (parser != NULL);
+ g_assert (data != NULL);
+
+ if (g_strcmp0 (tagname, "items") == 0)
+ {
+ ItemParserData *parser_data;
+
+ parser_data = g_slice_new0 (ItemParserData);
+ parser_data->self = self;
+
+ *parser = ItemParser;
+ *data = parser_data;
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+egg_radio_box_custom_finished (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ GObject *child,
+ const gchar *tagname,
+ gpointer user_data)
+{
+ EggRadioBox *self = (EggRadioBox *)buildable;
+
+ g_assert (EGG_IS_RADIO_BOX (self));
+ g_assert (GTK_IS_BUILDER (builder));
+ g_assert (tagname != NULL);
+
+ if (g_strcmp0 (tagname, "items") == 0)
+ {
+ ItemParserData *parser_data = user_data;
+
+ g_free (parser_data->id);
+ g_string_free (parser_data->text, TRUE);
+ g_slice_free (ItemParserData, parser_data);
+ }
+}
+
+static void
+buildable_iface_init (GtkBuildableIface *iface)
+{
+ iface->custom_tag_start = egg_radio_box_custom_tag_start;
+ iface->custom_finished = egg_radio_box_custom_finished;
+}
diff --git a/contrib/egg/egg-radio-box.h b/contrib/egg/egg-radio-box.h
new file mode 100644
index 0000000..c569ebf
--- /dev/null
+++ b/contrib/egg/egg-radio-box.h
@@ -0,0 +1,50 @@
+/* egg-radio-box.h
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EGG_RADIO_BOX_H
+#define EGG_RADIO_BOX_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define EGG_TYPE_RADIO_BOX (egg_radio_box_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (EggRadioBox, egg_radio_box, EGG, RADIO_BOX, GtkBin)
+
+struct _EggRadioBoxClass
+{
+ GtkBinClass parent_class;
+
+ gpointer _padding1;
+ gpointer _padding2;
+ gpointer _padding3;
+ gpointer _padding4;
+};
+
+GtkWidget *egg_radio_box_new (void);
+void egg_radio_box_add_item (EggRadioBox *self,
+ const gchar *id,
+ const gchar *text);
+gchar *egg_radio_box_get_active_id (EggRadioBox *self);
+void egg_radio_box_set_active_id (EggRadioBox *self,
+ const gchar *id);
+
+G_END_DECLS
+
+#endif /* EGG_RADIO_BOX_H */
diff --git a/tests/Makefile.am b/tests/Makefile.am
index a2d45da..f2c7951 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -204,6 +204,12 @@ test_egg_state_machine_CFLAGS = $(egg_cflags)
test_egg_state_machine_LDADD = $(egg_libs)
+TESTS += test-egg-radio-box
+test_egg_radio_box_SOURCES = test-egg-radio-box.c
+test_egg_radio_box_CFLAGS = $(egg_cflags)
+test_egg_radio_box_LDADD = $(egg_libs)
+
+
TESTS += test-egg-cache
test_egg_cache_SOURCES = test-egg-cache.c
test_egg_cache_CFLAGS = $(egg_cflags)
diff --git a/tests/test-egg-radio-box.c b/tests/test-egg-radio-box.c
new file mode 100644
index 0000000..87ea675
--- /dev/null
+++ b/tests/test-egg-radio-box.c
@@ -0,0 +1,34 @@
+#include <egg-radio-box.h>
+#include <stdlib.h>
+
+gint
+main (gint argc,
+ gchar *argv[])
+{
+ GtkWindow *window;
+ EggRadioBox *box;
+
+ gtk_init (&argc, &argv);
+
+ window = g_object_new (GTK_TYPE_WINDOW,
+ "title", "Test EggRadioBox",
+ NULL);
+ box = g_object_new (EGG_TYPE_RADIO_BOX,
+ "visible", TRUE,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (box));
+
+ egg_radio_box_add_item (box, "1", "One");
+ egg_radio_box_add_item (box, "2", "Two");
+ egg_radio_box_add_item (box, "3", "Three");
+ egg_radio_box_add_item (box, "4", "Four");
+ egg_radio_box_add_item (box, "5", "Five");
+ egg_radio_box_add_item (box, "6", "Six");
+
+ g_signal_connect (window, "delete-event", gtk_main_quit, NULL);
+ gtk_window_present (window);
+
+ gtk_main ();
+
+ return EXIT_SUCCESS;
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]