[gedit] Change the switcher to manually use a popover



commit ed8070f1428687768f3d2a85a5f89b10cb660a07
Author: Paolo Borelli <pborelli gnome org>
Date:   Sun Feb 16 22:43:01 2014 +0100

    Change the switcher to manually use a popover
    
    GPropertyActions is causing us more trouble than it is worth (because
    the property can be NULL). Beside we prefer a design that shows
    real buttons instead of radio items.

 gedit/gedit-menu-stack-switcher.c |  392 ++++++++++++++++++++++---------------
 1 files changed, 233 insertions(+), 159 deletions(-)
---
diff --git a/gedit/gedit-menu-stack-switcher.c b/gedit/gedit-menu-stack-switcher.c
index a07de09..6f2528f 100644
--- a/gedit/gedit-menu-stack-switcher.c
+++ b/gedit/gedit-menu-stack-switcher.c
@@ -22,22 +22,19 @@
 #include <config.h>
 #endif
 
-#include "gedit-menu-stack-switcher.h"
-
 #include <glib.h>
 #include <glib/gi18n.h>
 #include <gtk/gtk.h>
+#include "gedit-menu-stack-switcher.h"
 
 struct _GeditMenuStackSwitcherPrivate
 {
-  GtkWidget *label;
-
   GtkStack *stack;
-  GSimpleActionGroup *action_group;
-  GMenu *menu;
-
-  GtkWidget *current_child;
-  gulong signal_handler;
+  GtkWidget *label;
+  GtkWidget *button_box;
+  GtkWidget *popover;
+  GHashTable *buttons;
+  gboolean in_child_changed;
 };
 
 enum {
@@ -47,202 +44,215 @@ enum {
 
 G_DEFINE_TYPE_WITH_PRIVATE (GeditMenuStackSwitcher, gedit_menu_stack_switcher, GTK_TYPE_MENU_BUTTON)
 
-static void update_label (GeditMenuStackSwitcher *switcher);
-
 static void
-gedit_menu_stack_switcher_get_property (GObject    *object,
-                                        guint       prop_id,
-                                        GValue     *value,
-                                        GParamSpec *pspec)
+gedit_menu_stack_switcher_init (GeditMenuStackSwitcher *switcher)
 {
-  GeditMenuStackSwitcher *switcher = GEDIT_MENU_STACK_SWITCHER (object);
-  GeditMenuStackSwitcherPrivate *priv = switcher->priv;
+  GeditMenuStackSwitcherPrivate *priv;
+  GtkWidget *box;
+  GtkWidget *arrow;
 
-  switch (prop_id)
-    {
-    case PROP_STACK:
-      g_value_set_object (value, priv->stack);
-      break;
+  priv = gedit_menu_stack_switcher_get_instance_private (switcher);
+  switcher->priv = priv;
 
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-    }
+  box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+
+  arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
+  g_object_bind_property (switcher, "direction", arrow, "arrow-type", 0);
+  gtk_box_pack_end (GTK_BOX (box), arrow, FALSE, TRUE, 6);
+
+  priv->label = gtk_label_new (NULL);
+  gtk_box_pack_start (GTK_BOX (box), priv->label, TRUE, TRUE, 6);
+
+  gtk_widget_show_all (box);
+  gtk_container_add (GTK_CONTAINER (switcher), box);
+
+  priv->popover = gtk_popover_new (GTK_WIDGET (switcher));
+  gtk_popover_set_position (GTK_POPOVER (priv->popover), GTK_POS_BOTTOM);
+
+  priv->button_box = gtk_button_box_new (GTK_ORIENTATION_VERTICAL);
+
+  g_object_set (priv->button_box,
+                "margin-start", 12,
+                "margin-end", 12,
+                "margin-top", 12,
+                "margin-bottom", 12,
+                NULL);
+
+  gtk_widget_show (priv->button_box);
+
+  gtk_container_add (GTK_CONTAINER (priv->popover), priv->button_box);
+
+  gtk_menu_button_set_popover (GTK_MENU_BUTTON (switcher), priv->popover);
+
+  priv->buttons = g_hash_table_new (g_direct_hash, g_direct_equal);
 }
 
 static void
-gedit_menu_stack_switcher_set_property (GObject      *object,
-                                        guint         prop_id,
-                                        const GValue *value,
-                                        GParamSpec   *pspec)
+clear_popover (GeditMenuStackSwitcher *switcher)
 {
-  GeditMenuStackSwitcher *switcher = GEDIT_MENU_STACK_SWITCHER (object);
-
-  switch (prop_id)
-    {
-    case PROP_STACK:
-      gedit_menu_stack_switcher_set_stack (switcher, g_value_get_object (value));
-      break;
+  GeditMenuStackSwitcherPrivate *priv = switcher->priv;
 
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-    }
+  gtk_container_foreach (GTK_CONTAINER (priv->button_box), (GtkCallback) gtk_widget_destroy, switcher);
 }
 
 static void
-gedit_menu_stack_switcher_dispose (GObject *object)
+on_button_clicked (GtkWidget              *widget,
+                   GeditMenuStackSwitcher *switcher)
 {
-  GeditMenuStackSwitcher *switcher = GEDIT_MENU_STACK_SWITCHER (object);
-
-  gedit_menu_stack_switcher_set_stack (switcher, NULL);
+  GeditMenuStackSwitcherPrivate *priv = switcher->priv;
+  GtkWidget *child;
 
-  G_OBJECT_CLASS (gedit_menu_stack_switcher_parent_class)->dispose (object);
+  if (!priv->in_child_changed)
+    {
+      child = g_object_get_data (G_OBJECT (widget), "stack-child");
+      gtk_stack_set_visible_child (priv->stack, child);
+    }
 }
 
 static void
-add_menu_entry (GtkWidget              *widget,
-                GeditMenuStackSwitcher *switcher)
+update_button (GeditMenuStackSwitcher *switcher,
+               GtkWidget              *widget,
+               GtkWidget              *button)
 {
   GeditMenuStackSwitcherPrivate *priv = switcher->priv;
-  gchar *title, *name;
-  GMenuItem *item;
-
-  if (!gtk_widget_get_visible(widget))
-    return;
+  gchar *title;
 
   gtk_container_child_get (GTK_CONTAINER (priv->stack), widget,
                            "title", &title,
-                           "name", &name,
                            NULL);
 
-  item = g_menu_item_new (title, NULL);
-  g_menu_item_set_action_and_target (item, "switcher.set-visible-child", "s", name);
+  gtk_button_set_label (GTK_BUTTON (button), title);
+  gtk_widget_set_visible (button, gtk_widget_get_visible (widget) && (title != NULL));
+  gtk_widget_set_size_request (button, 100, -1);
 
-  g_free (title);
-  g_free (name);
+  if (widget == gtk_stack_get_visible_child (priv->stack))
+    {
+      gtk_label_set_label (GTK_LABEL (priv->label), title);
+    }
 
-  g_menu_append_item (priv->menu, item);
-  g_object_unref (item);
+  g_free (title);
 }
 
-static gboolean
-reset_menu (GeditMenuStackSwitcher *switcher)
+static void
+on_title_icon_visible_updated (GtkWidget              *widget,
+                               GParamSpec             *pspec,
+                               GeditMenuStackSwitcher *switcher)
 {
-  g_menu_remove_all (switcher->priv->menu);
+  GeditMenuStackSwitcherPrivate *priv = switcher->priv;
+  GtkWidget *button;
 
-  return FALSE;
+  button = g_hash_table_lookup (priv->buttons, widget);
+  update_button (switcher, widget, button);
 }
 
 static void
-gedit_menu_stack_switcher_toggled (GtkToggleButton *button)
+on_position_updated (GtkWidget        *widget,
+                     GParamSpec       *pspec,
+                     GeditMenuStackSwitcher *switcher)
 {
-  GeditMenuStackSwitcher *switcher = GEDIT_MENU_STACK_SWITCHER (button);
   GeditMenuStackSwitcherPrivate *priv = switcher->priv;
+  GtkWidget *button;
+  gint position;
 
-  if (!priv->stack)
-    return;
-
-  if (gtk_toggle_button_get_active (button))
-    gtk_container_foreach (GTK_CONTAINER (priv->stack), (GtkCallback) add_menu_entry, switcher);
+  button = g_hash_table_lookup (priv->buttons, widget);
 
-  GTK_TOGGLE_BUTTON_CLASS (gedit_menu_stack_switcher_parent_class)->toggled (button);
+  gtk_container_child_get (GTK_CONTAINER (priv->stack), widget,
+                           "position", &position,
+                           NULL);
 
-  if (!gtk_toggle_button_get_active (button))
-    g_idle_add ((GSourceFunc) reset_menu, switcher);
+  gtk_box_reorder_child (GTK_BOX (priv->button_box), button, position);
 }
 
 static void
-gedit_menu_stack_switcher_class_init (GeditMenuStackSwitcherClass *klass)
+add_child (GeditMenuStackSwitcher *switcher,
+           GtkWidget              *widget)
 {
-  GObjectClass *object_class = G_OBJECT_CLASS (klass);
-  GtkToggleButtonClass *toggle_button_class = GTK_TOGGLE_BUTTON_CLASS (klass);
-
-  object_class->get_property = gedit_menu_stack_switcher_get_property;
-  object_class->set_property = gedit_menu_stack_switcher_set_property;
-  object_class->dispose = gedit_menu_stack_switcher_dispose;
-
-  toggle_button_class->toggled = gedit_menu_stack_switcher_toggled;
+  GeditMenuStackSwitcherPrivate *priv = switcher->priv;
+  GtkWidget *button;
+  GList *group;
 
-  g_object_class_install_property (object_class,
-                                   PROP_STACK,
-                                   g_param_spec_object ("stack",
-                                                        "Stack",
-                                                        "Stack",
-                                                        GTK_TYPE_STACK,
-                                                        G_PARAM_READWRITE |
-                                                        G_PARAM_CONSTRUCT));
-}
+  button = gtk_radio_button_new (NULL);
+  gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (button), FALSE);
+  gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
 
-static void
-gedit_menu_stack_switcher_init (GeditMenuStackSwitcher *switcher)
-{
-  GeditMenuStackSwitcherPrivate *priv;
-  GtkWidget *box;
-  GtkWidget *arrow;
+  g_object_set (button,
+                "margin-top", 3,
+                "margin-bottom", 3,
+                NULL);
 
-  priv = gedit_menu_stack_switcher_get_instance_private (switcher);
-  switcher->priv = priv;
+  update_button (switcher, widget, button);
 
-  box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+  group = gtk_container_get_children (GTK_CONTAINER (priv->button_box));
+  if (group != NULL)
+    {
+      gtk_radio_button_join_group (GTK_RADIO_BUTTON (button), GTK_RADIO_BUTTON (group->data));
+      g_list_free (group);
+    }
 
-  arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
-  g_object_bind_property (switcher, "direction", arrow, "arrow-type", 0);
-  gtk_box_pack_end (GTK_BOX (box), arrow, FALSE, TRUE, 6);
+  gtk_container_add (GTK_CONTAINER (priv->button_box), button);
 
-  priv->label = gtk_label_new (NULL);
-  gtk_box_pack_start (GTK_BOX (box), priv->label, TRUE, TRUE, 6);
+  g_object_set_data (G_OBJECT (button), "stack-child", widget);
+  g_signal_connect (button, "clicked", G_CALLBACK (on_button_clicked), switcher);
+  g_signal_connect (widget, "notify::visible", G_CALLBACK (on_title_icon_visible_updated), switcher);
+  g_signal_connect (widget, "child-notify::title", G_CALLBACK (on_title_icon_visible_updated), switcher);
+  g_signal_connect (widget, "child-notify::icon-name", G_CALLBACK (on_title_icon_visible_updated), switcher);
+  g_signal_connect (widget, "child-notify::position", G_CALLBACK (on_position_updated), switcher);
 
-  gtk_widget_show_all (box);
-  gtk_container_add (GTK_CONTAINER (switcher), box);
+  g_hash_table_insert (priv->buttons, widget, button);
 }
 
 static void
-disconnect_stack_child_signals (GeditMenuStackSwitcher *switcher)
+foreach_stack (GtkWidget              *widget,
+               GeditMenuStackSwitcher *switcher)
 {
-  GeditMenuStackSwitcherPrivate *priv = switcher->priv;
-
-  if (priv->current_child && priv->signal_handler)
-    g_signal_handler_disconnect (priv->current_child, priv->signal_handler);
-
-  priv->signal_handler = 0;
-  priv->current_child = NULL;
+  add_child (switcher, widget);
 }
 
 static void
-connect_stack_child_signals (GeditMenuStackSwitcher *switcher,
-                             GtkWidget *child)
+populate_popover (GeditMenuStackSwitcher *switcher)
 {
   GeditMenuStackSwitcherPrivate *priv = switcher->priv;
 
-  disconnect_stack_child_signals (switcher);
-
-  if (child)
-    priv->signal_handler = g_signal_connect_swapped (child, "child-notify::title",
-                                                     G_CALLBACK (update_label), switcher);
-
-  priv->current_child = child;
+  gtk_container_foreach (GTK_CONTAINER (priv->stack), (GtkCallback)foreach_stack, switcher);
 }
 
 static void
-update_label (GeditMenuStackSwitcher *switcher)
+on_child_changed (GtkWidget              *widget,
+                  GParamSpec             *pspec,
+                  GeditMenuStackSwitcher *switcher)
 {
   GeditMenuStackSwitcherPrivate *priv = switcher->priv;
-  GtkWidget *child = NULL;
-  gchar *title = NULL;
+  GtkWidget *child;
+  GtkWidget *button;
 
-  if (priv->stack)
-    child = gtk_stack_get_visible_child (GTK_STACK (priv->stack));
+  child = gtk_stack_get_visible_child (GTK_STACK (widget));
+  if (child)
+    {
+      gchar *title;
 
-  if (child != priv->current_child)
-    connect_stack_child_signals (switcher, child);
+      gtk_container_child_get (GTK_CONTAINER (priv->stack), child,
+                               "title", &title,
+                               NULL);
 
-  if (child)
-    gtk_container_child_get (GTK_CONTAINER (priv->stack), child,
-                             "title", &title, NULL);
+      gtk_label_set_label (GTK_LABEL (priv->label), title);
+      g_free (title);
+    }
 
-  gtk_label_set_label (GTK_LABEL (priv->label), title);
-  g_free (title);
+  button = g_hash_table_lookup (priv->buttons, child);
+  if (button != NULL)
+    {
+      priv->in_child_changed = TRUE;
+      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
+      priv->in_child_changed = FALSE;
+    }
+}
+
+static void
+on_stack_child_added (GtkStack               *stack,
+                      GtkWidget              *widget,
+                      GeditMenuStackSwitcher *switcher)
+{
+  add_child (switcher, widget);
 }
 
 static void
@@ -251,9 +261,11 @@ on_stack_child_removed (GtkStack               *stack,
                         GeditMenuStackSwitcher *switcher)
 {
   GeditMenuStackSwitcherPrivate *priv = switcher->priv;
+  GtkWidget *button;
 
-  if (widget == priv->current_child)
-    disconnect_stack_child_signals (switcher);
+  button = g_hash_table_lookup (priv->buttons, widget);
+  gtk_container_remove (GTK_CONTAINER (priv->button_box), button);
+  g_hash_table_remove (priv->buttons, widget);
 }
 
 static void
@@ -261,8 +273,10 @@ disconnect_stack_signals (GeditMenuStackSwitcher *switcher)
 {
   GeditMenuStackSwitcherPrivate *priv = switcher->priv;
 
-  g_signal_handlers_disconnect_by_func (priv->stack, update_label, switcher);
+  g_signal_handlers_disconnect_by_func (priv->stack, on_stack_child_added, switcher);
   g_signal_handlers_disconnect_by_func (priv->stack, on_stack_child_removed, switcher);
+  g_signal_handlers_disconnect_by_func (priv->stack, on_child_changed, switcher);
+  g_signal_handlers_disconnect_by_func (priv->stack, disconnect_stack_signals, switcher);
 }
 
 static void
@@ -270,10 +284,14 @@ connect_stack_signals (GeditMenuStackSwitcher *switcher)
 {
   GeditMenuStackSwitcherPrivate *priv = switcher->priv;
 
-  g_signal_connect_swapped (priv->stack, "notify::visible-child",
-                            G_CALLBACK (update_label), switcher);
+  g_signal_connect (priv->stack, "add",
+                    G_CALLBACK (on_stack_child_added), switcher);
   g_signal_connect (priv->stack, "remove",
                     G_CALLBACK (on_stack_child_removed), switcher);
+  g_signal_connect (priv->stack, "notify::visible-child",
+                    G_CALLBACK (on_child_changed), switcher);
+  g_signal_connect_swapped (priv->stack, "destroy",
+                            G_CALLBACK (disconnect_stack_signals), switcher);
 }
 
 void
@@ -281,7 +299,6 @@ gedit_menu_stack_switcher_set_stack (GeditMenuStackSwitcher *switcher,
                                      GtkStack               *stack)
 {
   GeditMenuStackSwitcherPrivate *priv;
-  GPropertyAction *action;
 
   g_return_if_fail (GEDIT_IS_MENU_STACK_SWITCHER (switcher));
   g_return_if_fail (stack == NULL || GTK_IS_STACK (stack));
@@ -293,32 +310,19 @@ gedit_menu_stack_switcher_set_stack (GeditMenuStackSwitcher *switcher,
 
   if (priv->stack)
     {
-      gtk_widget_insert_action_group (GTK_WIDGET (switcher), "switcher", NULL);
-      g_clear_object (&priv->action_group);
-      priv->menu = NULL;
-
       disconnect_stack_signals (switcher);
-      disconnect_stack_child_signals (switcher);
+      clear_popover (switcher);
       g_clear_object (&priv->stack);
     }
 
   if (stack)
     {
       priv->stack = g_object_ref (stack);
+      populate_popover (switcher);
       connect_stack_signals (switcher);
-
-      priv->action_group = g_simple_action_group_new ();
-      gtk_widget_insert_action_group (GTK_WIDGET (switcher), "switcher", G_ACTION_GROUP 
(priv->action_group));
-
-      action = g_property_action_new ("set-visible-child", priv->stack, "visible-child-name");
-      g_action_map_add_action (G_ACTION_MAP (priv->action_group), G_ACTION (action));
-      g_object_unref (action);
-
-      priv->menu = g_menu_new ();
     }
 
-  gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (switcher), G_MENU_MODEL (priv->menu));
-  update_label (switcher);
+  gtk_widget_queue_resize (GTK_WIDGET (switcher));
 
   g_object_notify (G_OBJECT (switcher), "stack");
 }
@@ -331,6 +335,76 @@ gedit_menu_stack_switcher_get_stack (GeditMenuStackSwitcher *switcher)
   return switcher->priv->stack;
 }
 
+static void
+gedit_menu_stack_switcher_get_property (GObject    *object,
+                                        guint       prop_id,
+                                        GValue     *value,
+                                        GParamSpec *pspec)
+{
+  GeditMenuStackSwitcher *switcher = GEDIT_MENU_STACK_SWITCHER (object);
+  GeditMenuStackSwitcherPrivate *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
+gedit_menu_stack_switcher_set_property (GObject      *object,
+                                        guint         prop_id,
+                                        const GValue *value,
+                                        GParamSpec   *pspec)
+{
+  GeditMenuStackSwitcher *switcher = GEDIT_MENU_STACK_SWITCHER (object);
+
+  switch (prop_id)
+    {
+    case PROP_STACK:
+      gedit_menu_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
+gedit_menu_stack_switcher_dispose (GObject *object)
+{
+  GeditMenuStackSwitcher *switcher = GEDIT_MENU_STACK_SWITCHER (object);
+
+  gedit_menu_stack_switcher_set_stack (switcher, NULL);
+
+  G_OBJECT_CLASS (gedit_menu_stack_switcher_parent_class)->dispose (object);
+}
+
+static void
+gedit_menu_stack_switcher_class_init (GeditMenuStackSwitcherClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->get_property = gedit_menu_stack_switcher_get_property;
+  object_class->set_property = gedit_menu_stack_switcher_set_property;
+  object_class->dispose = gedit_menu_stack_switcher_dispose;
+
+  g_object_class_install_property (object_class,
+                                   PROP_STACK,
+                                   g_param_spec_object ("stack",
+                                                        "Stack",
+                                                        "Stack",
+                                                        GTK_TYPE_STACK,
+                                                        G_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT));
+}
+
 GtkWidget *
 gedit_menu_stack_switcher_new (void)
 {


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]