[libgd] Add GdStackSwitcher
- From: William Jon McCann <mccann src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libgd] Add GdStackSwitcher
- Date: Mon, 18 Feb 2013 22:04:32 +0000 (UTC)
commit 4c440146ac9714a8f718734b6333cca715244500
Author: William Jon McCann <jmccann redhat com>
Date: Mon Feb 18 00:57:27 2013 -0500
Add GdStackSwitcher
https://bugzilla.gnome.org/show_bug.cgi?id=694036
Makefile.am | 2 +
libgd/gd-stack-switcher.c | 334 +++++++++++++++++++++++++++++++++++++++++++++
libgd/gd-stack-switcher.h | 66 +++++++++
libgd/gd-stack.c | 59 +++++++--
test-stack.c | 11 ++-
5 files changed, 462 insertions(+), 10 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 45a2f24..4cfcbb2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -156,6 +156,8 @@ if LIBGD_STACK
stack_sources = \
libgd/gd-stack.c \
libgd/gd-stack.h \
+ libgd/gd-stack-switcher.c \
+ libgd/gd-stack-switcher.h \
$(NULL)
nodist_libgd_la_SOURCES += $(stack_sources)
diff --git a/libgd/gd-stack-switcher.c b/libgd/gd-stack-switcher.c
new file mode 100644
index 0000000..28c5809
--- /dev/null
+++ b/libgd/gd-stack-switcher.c
@@ -0,0 +1,334 @@
+/*
+ * Copyright (c) 2013 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 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 Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "gd-stack-switcher.h"
+
+#define P_(s) s
+
+struct _GdStackSwitcherPrivate
+{
+ GdStack *stack;
+ GHashTable *buttons;
+};
+
+enum {
+ PROP_0,
+ PROP_STACK
+};
+
+G_DEFINE_TYPE (GdStackSwitcher, gd_stack_switcher, GTK_TYPE_BOX);
+
+static void
+gd_stack_switcher_init (GdStackSwitcher *switcher)
+{
+ GtkStyleContext *context;
+ GdStackSwitcherPrivate *priv;
+
+ priv = G_TYPE_INSTANCE_GET_PRIVATE (switcher, GD_TYPE_STACK_SWITCHER, GdStackSwitcherPrivate);
+ switcher->priv = priv;
+
+ priv->stack = NULL;
+ priv->buttons = g_hash_table_new (g_direct_hash, g_direct_equal);
+
+ context = gtk_widget_get_style_context (GTK_WIDGET (switcher));
+ gtk_style_context_add_class (context, GTK_STYLE_CLASS_LINKED);
+
+ gtk_orientable_set_orientation (GTK_ORIENTABLE (switcher), GTK_ORIENTATION_HORIZONTAL);
+}
+
+static void
+clear_switcher (GdStackSwitcher *self)
+{
+ gtk_container_foreach (GTK_CONTAINER (self), (GtkCallback) gtk_widget_destroy, self);
+}
+
+static void
+on_button_clicked (GtkWidget *widget,
+ GdStackSwitcher *self)
+{
+ GtkWidget *child;
+
+ child = g_object_get_data (G_OBJECT (widget), "stack-child");
+ gd_stack_set_visible_child (self->priv->stack, child);
+}
+
+static void
+on_title_updated (GtkWidget *widget,
+ GParamSpec *pspec,
+ GdStackSwitcher *self)
+
+{
+ char *title;
+ GtkWidget *button;
+
+ gtk_container_child_get (GTK_CONTAINER (self->priv->stack), widget,
+ "title", &title,
+ NULL);
+ button = g_hash_table_lookup (self->priv->buttons, widget);
+ gtk_button_set_label (GTK_BUTTON (button), title);
+ g_free (title);
+}
+
+static void
+add_child (GdStackSwitcher *self,
+ GtkWidget *widget)
+{
+ GtkWidget *button;
+ GList *group;
+ char *title = NULL;
+
+ gtk_container_child_get (GTK_CONTAINER (self->priv->stack), widget,
+ "title", &title,
+ NULL);
+
+ button = gtk_radio_button_new (NULL);
+ if (title != NULL && title[0] != '\0')
+ gtk_button_set_label (GTK_BUTTON (button), title);
+
+ g_free (title);
+
+ gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (button), FALSE);
+ gtk_widget_set_size_request (button, 100, -1);
+ gtk_widget_set_vexpand (button, TRUE);
+ gtk_style_context_add_class (gtk_widget_get_style_context (button), "raised");
+ gtk_style_context_add_class (gtk_widget_get_style_context (button), "text-button");
+
+ group = gtk_container_get_children (GTK_CONTAINER (self));
+ if (group != NULL)
+ {
+ gtk_radio_button_join_group (GTK_RADIO_BUTTON (button), GTK_RADIO_BUTTON (group->data));
+ g_list_free (group);
+ }
+
+ gtk_container_add (GTK_CONTAINER (self), button);
+ gtk_widget_show (button);
+
+ g_object_set_data (G_OBJECT (button), "stack-child", widget);
+ g_signal_connect (button, "clicked", G_CALLBACK (on_button_clicked), self);
+ g_signal_connect (widget, "child-notify::title", G_CALLBACK (on_title_updated), self);
+
+ g_hash_table_insert (self->priv->buttons, widget, button);
+}
+
+static void
+foreach_stack (GtkWidget *widget,
+ GdStackSwitcher *self)
+{
+ add_child (self, widget);
+}
+
+static void
+populate_switcher (GdStackSwitcher *self)
+{
+ gtk_container_foreach (GTK_CONTAINER (self->priv->stack), (GtkCallback)foreach_stack, self);
+}
+
+static void
+on_child_changed (GtkWidget *widget,
+ GParamSpec *pspec,
+ GdStackSwitcher *self)
+{
+ GtkWidget *child;
+ GtkWidget *button;
+
+ child = gd_stack_get_visible_child (GD_STACK (widget));
+ button = g_hash_table_lookup (self->priv->buttons, child);
+ if (button != NULL)
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
+}
+
+static void
+on_stack_child_added (GtkContainer *container,
+ GtkWidget *widget,
+ GdStackSwitcher *self)
+{
+ add_child (self, widget);
+}
+
+static void
+on_stack_child_removed (GtkContainer *container,
+ GtkWidget *widget,
+ GdStackSwitcher *self)
+{
+ GtkWidget *button;
+
+ button = g_hash_table_lookup (self->priv->buttons, widget);
+ gtk_container_remove (GTK_CONTAINER (self), button);
+ g_hash_table_remove (self->priv->buttons, widget);
+}
+
+static void
+disconnect_stack_signals (GdStackSwitcher *self)
+{
+ g_signal_handlers_disconnect_by_func (self->priv->stack, G_CALLBACK (on_stack_child_added), self);
+ g_signal_handlers_disconnect_by_func (self->priv->stack, G_CALLBACK (on_stack_child_removed), self);
+ g_signal_handlers_disconnect_by_func (self->priv->stack, G_CALLBACK (on_child_changed), self);
+}
+
+static void
+connect_stack_signals (GdStackSwitcher *self)
+{
+ g_signal_connect_after (self->priv->stack, "add", G_CALLBACK (on_stack_child_added), self);
+ g_signal_connect_after (self->priv->stack, "remove", G_CALLBACK (on_stack_child_removed), self);
+ g_signal_connect (self->priv->stack, "notify::visible-child", G_CALLBACK (on_child_changed), self);
+}
+
+/**
+ * gd_stack_switcher_set_stack:
+ * @switcher: a #GdStackSwitcher
+ * @stack: (allow-none): a #GdStack
+ *
+ * Sets the stack to control.
+ *
+ **/
+void
+gd_stack_switcher_set_stack (GdStackSwitcher *switcher,
+ GdStack *stack)
+{
+ GdStackSwitcherPrivate *priv;
+
+ g_return_if_fail (GD_IS_STACK_SWITCHER (switcher));
+ if (stack)
+ g_return_if_fail (GD_IS_STACK (stack));
+
+ priv = switcher->priv;
+
+ if (priv->stack == stack)
+ return;
+
+ if (priv->stack)
+ {
+ disconnect_stack_signals (switcher);
+ clear_switcher (switcher);
+ g_clear_object (&priv->stack);
+ }
+
+ if (stack)
+ {
+ priv->stack = g_object_ref (stack);
+ populate_switcher (switcher);
+ connect_stack_signals (switcher);
+ }
+
+ gtk_widget_queue_resize (GTK_WIDGET (switcher));
+
+ g_object_notify (G_OBJECT (switcher), "stack");
+}
+
+/**
+ * gd_stack_switcher_get_stack:
+ * @switcher: a #GdStackSwitcher
+ *
+ * Retrieves the stack. See
+ * gd_stack_switcher_set_stack().
+ *
+ * Return value: (transfer none): the stack, or %NULL if
+ * none has been set explicitly.
+ **/
+GdStack *
+gd_stack_switcher_get_stack (GdStackSwitcher *switcher)
+{
+ g_return_val_if_fail (GD_IS_STACK_SWITCHER (switcher), NULL);
+
+ return switcher->priv->stack;
+}
+
+static void
+gd_stack_switcher_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GdStackSwitcher *switcher = GD_STACK_SWITCHER (object);
+ GdStackSwitcherPrivate *priv = switcher->priv;
+
+ switch (prop_id)
+ {
+ case PROP_STACK:
+ g_value_set_object (value, priv->stack);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gd_stack_switcher_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GdStackSwitcher *switcher = GD_STACK_SWITCHER (object);
+ GdStackSwitcherPrivate *priv = switcher->priv;
+
+ switch (prop_id)
+ {
+ case PROP_STACK:
+ gd_stack_switcher_set_stack (switcher, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gd_stack_switcher_finalize (GObject *object)
+{
+ GdStackSwitcher *self = GD_STACK_SWITCHER (object);
+ GdStackSwitcherPrivate *priv = self->priv;
+
+ g_hash_table_destroy (priv->buttons);
+ g_clear_object (&priv->stack);
+
+ G_OBJECT_CLASS (gd_stack_switcher_parent_class)->finalize (object);
+}
+
+static void
+gd_stack_switcher_class_init (GdStackSwitcherClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
+ GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
+
+ object_class->get_property = gd_stack_switcher_get_property;
+ object_class->set_property = gd_stack_switcher_set_property;
+ object_class->finalize = gd_stack_switcher_finalize;
+
+ g_object_class_install_property (object_class,
+ PROP_STACK,
+ g_param_spec_object ("stack",
+ P_("Stack"),
+ P_("Stack"),
+ GD_TYPE_STACK,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ g_type_class_add_private (object_class, sizeof (GdStackSwitcherPrivate));
+}
+
+GtkWidget *
+gd_stack_switcher_new (void)
+{
+ return GTK_WIDGET (g_object_new (GD_TYPE_STACK_SWITCHER, NULL));
+}
diff --git a/libgd/gd-stack-switcher.h b/libgd/gd-stack-switcher.h
new file mode 100644
index 0000000..2cdbba5
--- /dev/null
+++ b/libgd/gd-stack-switcher.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2013 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 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 Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef __GD_STACK_SWITCHER_H__
+#define __GD_STACK_SWITCHER_H__
+
+#include <gtk/gtk.h>
+#include <libgd/gd-stack.h>
+
+G_BEGIN_DECLS
+
+#define GD_TYPE_STACK_SWITCHER (gd_stack_switcher_get_type ())
+#define GD_STACK_SWITCHER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GD_TYPE_STACK_SWITCHER,
GdStackSwitcher))
+#define GD_STACK_SWITCHER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GD_TYPE_STACK_SWITCHER,
GdStackSwitcherClass))
+#define GD_IS_STACK_SWITCHER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GD_TYPE_STACK_SWITCHER))
+#define GD_IS_STACK_SWITCHER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GD_TYPE_STACK_SWITCHER))
+#define GD_STACK_SWITCHER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GD_TYPE_STACK_SWITCHER,
GdStackSwitcherClass))
+
+typedef struct _GdStackSwitcher GdStackSwitcher;
+typedef struct _GdStackSwitcherPrivate GdStackSwitcherPrivate;
+typedef struct _GdStackSwitcherClass GdStackSwitcherClass;
+
+struct _GdStackSwitcher
+{
+ GtkBox widget;
+
+ /*< private >*/
+ GdStackSwitcherPrivate *priv;
+};
+
+struct _GdStackSwitcherClass
+{
+ GtkBoxClass parent_class;
+
+ /* Padding for future expansion */
+ void (*_gd_reserved1) (void);
+ void (*_gd_reserved2) (void);
+ void (*_gd_reserved3) (void);
+ void (*_gd_reserved4) (void);
+};
+
+GType gd_stack_switcher_get_type (void) G_GNUC_CONST;
+GtkWidget *gd_stack_switcher_new (void);
+void gd_stack_switcher_set_stack (GdStackSwitcher *switcher,
+ GdStack *stack);
+GdStack *gd_stack_switcher_get_stack (GdStackSwitcher *switcher);
+
+G_END_DECLS
+
+#endif /* __GD_STACK_SWITCHER_H__ */
diff --git a/libgd/gd-stack.c b/libgd/gd-stack.c
index 9f3b3e1..faf3504 100644
--- a/libgd/gd-stack.c
+++ b/libgd/gd-stack.c
@@ -42,7 +42,8 @@ enum {
enum
{
CHILD_PROP_0,
- CHILD_PROP_NAME
+ CHILD_PROP_NAME,
+ CHILD_PROP_TITLE
};
typedef struct _GdStackChildInfo GdStackChildInfo;
@@ -50,6 +51,7 @@ typedef struct _GdStackChildInfo GdStackChildInfo;
struct _GdStackChildInfo {
GtkWidget *widget;
char *name;
+ char *title;
};
struct _GdStackPrivate {
@@ -273,6 +275,13 @@ gd_stack_class_init (GdStackClass * klass)
NULL,
GTK_PARAM_READWRITE));
+ gtk_container_class_install_child_property (container_class, CHILD_PROP_TITLE,
+ g_param_spec_string ("title",
+ "Title",
+ "The title of the child page",
+ NULL,
+ GTK_PARAM_READWRITE));
+
g_type_class_add_private (klass, sizeof (GdStackPrivate));
}
@@ -324,6 +333,10 @@ gd_stack_get_child_property (GtkContainer *container,
g_value_set_string (value, info->name);
break;
+ case CHILD_PROP_TITLE:
+ g_value_set_string (value, info->title);
+ break;
+
default:
GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
break;
@@ -351,13 +364,22 @@ gd_stack_set_child_property (GtkContainer *container,
switch (property_id)
{
case CHILD_PROP_NAME:
+ g_free (info->name);
info->name = g_value_dup_string (value);
+ gtk_container_child_notify (container, child, "name");
+
if (priv->visible_child == info)
g_object_notify (G_OBJECT (stack), "visible-child-name");
break;
+ case CHILD_PROP_TITLE:
+ g_free (info->title);
+ info->title = g_value_dup_string (value);
+ gtk_container_child_notify (container, child, "title");
+ break;
+
default:
GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
break;
@@ -564,10 +586,34 @@ stack_child_visibility_notify_cb (GObject *obj,
}
void
+gd_stack_add_titled (GdStack *stack,
+ GtkWidget *child,
+ const char *name,
+ const char *title)
+{
+ gtk_container_add_with_properties (GTK_CONTAINER (stack),
+ child,
+ "name", name,
+ "title", title,
+ NULL);
+}
+
+void
gd_stack_add_named (GdStack *stack,
GtkWidget *child,
const char *name)
{
+ gtk_container_add_with_properties (GTK_CONTAINER (stack),
+ child,
+ "name", name,
+ NULL);
+}
+
+static void
+gd_stack_add (GtkContainer *container,
+ GtkWidget *child)
+{
+ GdStack *stack = GD_STACK (container);
GdStackPrivate *priv = stack->priv;
GdStackChildInfo *child_info;
@@ -575,7 +621,8 @@ gd_stack_add_named (GdStack *stack,
child_info = g_slice_new (GdStackChildInfo);
child_info->widget = child;
- child_info->name = g_strdup (name);
+ child_info->name = NULL;
+ child_info->title = NULL;
priv->children = g_list_append (priv->children, child_info);
@@ -595,13 +642,6 @@ gd_stack_add_named (GdStack *stack,
}
static void
-gd_stack_add (GtkContainer *container,
- GtkWidget *child)
-{
- gd_stack_add_named (GD_STACK (container), child, NULL);
-}
-
-static void
gd_stack_remove (GtkContainer *container,
GtkWidget *child)
{
@@ -632,6 +672,7 @@ gd_stack_remove (GtkContainer *container,
gtk_widget_unparent (child);
+ g_free (child_info->title);
g_slice_free (GdStackChildInfo, child_info);
if (priv->homogeneous && was_visible)
diff --git a/test-stack.c b/test-stack.c
index e6ecac3..9960286 100644
--- a/test-stack.c
+++ b/test-stack.c
@@ -1,7 +1,9 @@
#include <gtk/gtk.h>
#include <libgd/gd-stack.h>
+#include <libgd/gd-stack-switcher.h>
GtkWidget *stack;
+GtkWidget *switcher;
static void
set_visible_child (GtkWidget *button, gpointer data)
@@ -38,23 +40,30 @@ main (gint argc,
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_container_add (GTK_CONTAINER (window), box);
+ switcher = gd_stack_switcher_new ();
+ gtk_box_pack_start (GTK_BOX (box), switcher, FALSE, FALSE, 0);
+
stack = gd_stack_new ();
gtk_widget_set_halign (stack, GTK_ALIGN_START);
gtk_container_add (GTK_CONTAINER (box), stack);
+ gd_stack_switcher_set_stack (GD_STACK_SWITCHER (switcher), GD_STACK (stack));
+
b1 = gtk_button_new_with_label ("Blah");
gtk_container_add_with_properties (GTK_CONTAINER (stack), b1,
"name", "1",
+ "title", "1",
NULL);
b2 = gtk_button_new_with_label ("Gazoooooooooooooooonk");
gtk_container_add (GTK_CONTAINER (stack), b2);
gtk_container_child_set (GTK_CONTAINER (stack), b2,
"name", "2",
+ "title", "2",
NULL);
b3 = gtk_button_new_with_label ("Foo\nBar");
- gd_stack_add_named (GD_STACK (stack), b3, "3");
+ gd_stack_add_titled (GD_STACK (stack), b3, "3", "3");
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_container_add (GTK_CONTAINER (box), hbox);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]