[gtk+/popover-menu-buttons2] Add support for extra widgets to model-based popovers



commit a9f6601368538f1008479d615d20da5195b8b385
Author: Matthias Clasen <mclasen redhat com>
Date:   Wed Apr 16 16:01:56 2014 -0700

    Add support for extra widgets to model-based popovers
    
    This allows to insert custom widgetry in the middle of model-based
    popovers.
    https://bugzilla.gnome.org/show_bug.cgi?id=727477

 gtk/gtkmenutrackeritem.c |    6 +++
 gtk/gtkmenutrackeritem.h |    2 +
 gtk/gtkpopover.c         |   45 ++++++++++++++++++++
 gtk/gtkpopover.h         |    9 ++++
 tests/popover.ui         |   17 ++++++++
 tests/testpopover.c      |  103 ++++++++++++++++++++++++++++++++++++++++++++-
 6 files changed, 179 insertions(+), 3 deletions(-)
---
diff --git a/gtk/gtkmenutrackeritem.c b/gtk/gtkmenutrackeritem.c
index 8477a6b..2ed6010 100644
--- a/gtk/gtkmenutrackeritem.c
+++ b/gtk/gtkmenutrackeritem.c
@@ -901,3 +901,9 @@ _gtk_menu_tracker_item_may_disappear (GtkMenuTrackerItem *self)
 {
   return self->hidden_when != HIDDEN_NEVER;
 }
+
+GMenuItem *
+gtk_menu_tracker_item_get_item (GtkMenuTrackerItem *self)
+{
+  return self->item;
+}
diff --git a/gtk/gtkmenutrackeritem.h b/gtk/gtkmenutrackeritem.h
index 2d9ff16..eebbccf 100644
--- a/gtk/gtkmenutrackeritem.h
+++ b/gtk/gtkmenutrackeritem.h
@@ -87,4 +87,6 @@ void                    gtk_menu_tracker_item_request_submenu_shown     (GtkMenu
 
 gboolean                gtk_menu_tracker_item_get_submenu_shown         (GtkMenuTrackerItem *self);
 
+GMenuItem              *gtk_menu_tracker_item_get_item                  (GtkMenuTrackerItem *self);
+
 #endif
diff --git a/gtk/gtkpopover.c b/gtk/gtkpopover.c
index d9ce632..cb9e6b8 100644
--- a/gtk/gtkpopover.c
+++ b/gtk/gtkpopover.c
@@ -1895,6 +1895,11 @@ gtk_popover_get_modal (GtkPopover *popover)
   return popover->priv->modal;
 }
 
+typedef struct {
+  GtkModelWidgetCallback *callback;
+  gpointer data;
+} TrackerData;
+
 static void
 gtk_popover_tracker_remove_func (gint     position,
                                  gpointer user_data)
@@ -1995,6 +2000,9 @@ gtk_popover_tracker_insert_func (GtkMenuTrackerItem *item,
   GtkWidget *stack;
   GtkWidget *widget;
   GtkSizeGroup *group;
+  TrackerData *data;
+
+  data = (TrackerData*)g_object_get_data (G_OBJECT (box), "trackerdata");
 
   stack = gtk_widget_get_ancestor (box, GTK_TYPE_STACK);
   group = g_object_get_data (G_OBJECT (stack), "size-group");
@@ -2045,6 +2053,7 @@ gtk_popover_tracker_insert_func (GtkMenuTrackerItem *item,
       GtkWidget *child;
       GtkWidget *button;
       GtkWidget *content;
+      TrackerData *tracker_data;
 
       child = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
       g_object_set (child, "margin", 10, NULL);
@@ -2084,12 +2093,33 @@ gtk_popover_tracker_insert_func (GtkMenuTrackerItem *item,
       gtk_widget_set_halign (content, GTK_ALIGN_FILL);
       gtk_widget_show (content);
       gtk_container_add (GTK_CONTAINER (child), content);
+      tracker_data = g_new0 (TrackerData, 1);
+      tracker_data->callback = data->callback;
+      tracker_data->data = data->data;
+      g_object_set_data_full (G_OBJECT (content), "trackerdata", tracker_data, g_free);
+
       tracker = gtk_menu_tracker_new_for_item_submenu (item, gtk_popover_tracker_insert_func, 
gtk_popover_tracker_remove_func, content);
 
       g_object_set_data_full (G_OBJECT (widget), "submenutracker", tracker, 
(GDestroyNotify)gtk_menu_tracker_free);
 
       gtk_widget_show (widget);
     }
+  else if (gtk_menu_tracker_item_get_special (item))
+    {
+      const gchar *special;
+
+      special = gtk_menu_tracker_item_get_special (item);
+      if (data->callback)
+        widget = data->callback (gtk_menu_tracker_item_get_item (item), data->data);
+      else
+        widget = NULL;
+
+      if (!widget)
+        {
+          g_warning ("No extra widget with id '%s' provided", special);      
+          return;
+        }
+    }
   else
     {
       widget = gtk_model_button_new ();
@@ -2158,12 +2188,23 @@ gtk_popover_bind_model (GtkPopover  *popover,
                         GMenuModel  *model,
                         const gchar *action_namespace)
 {
+  gtk_popover_bind_model_with_extra_widgets (popover, model, action_namespace, NULL, NULL);
+}
+
+void
+gtk_popover_bind_model_with_extra_widgets (GtkPopover  *popover,
+                                           GMenuModel  *model,
+                                           const gchar *action_namespace,
+                                           GtkModelWidgetCallback *callback,
+                                           gpointer               data)
+{
   GtkActionMuxer *muxer;
   GtkWidget *child;
   GtkWidget *stack;
   GtkWidget *box;
   GtkPopoverPrivate *priv;
   GtkSizeGroup *group;
+  TrackerData *tracker_data;
 
   g_return_if_fail (GTK_IS_POPOVER (popover));
   g_return_if_fail (model == NULL || G_IS_MENU_MODEL (model));
@@ -2197,6 +2238,10 @@ gtk_popover_bind_model (GtkPopover  *popover,
       g_signal_connect (popover, "unmap", G_CALLBACK (back_to_main), NULL);
       g_signal_connect (popover, "map", G_CALLBACK (back_to_main), NULL);
 
+      tracker_data = g_new0 (TrackerData, 1);
+      tracker_data->callback = callback;
+      tracker_data->data = data;
+      g_object_set_data_full (G_OBJECT (box), "trackerdata", tracker_data, g_free);
       priv->tracker = gtk_menu_tracker_new (GTK_ACTION_OBSERVABLE (muxer),
                                             model,
                                             TRUE,
diff --git a/gtk/gtkpopover.h b/gtk/gtkpopover.h
index d4baf82..59ecaac 100644
--- a/gtk/gtkpopover.h
+++ b/gtk/gtkpopover.h
@@ -97,6 +97,15 @@ void            gtk_popover_bind_model      (GtkPopover            *popover,
                                              GMenuModel            *model,
                                              const gchar           *action_namespace);
 
+typedef GtkWidget * (GtkModelWidgetCallback) (GMenuItem *item, gpointer data);
+
+GDK_AVAILABLE_IN_3_14
+void            gtk_popover_bind_model_with_extra_widgets (GtkPopover            *popover,
+                                                           GMenuModel            *model,
+                                                           const gchar           *action_namespace,
+                                                           GtkModelWidgetCallback *callback,
+                                                           gpointer               user_data);
+
 G_END_DECLS
 
 #endif /* __GTK_POPOVER_H__ */
diff --git a/tests/popover.ui b/tests/popover.ui
index 74ffdb9..3df020a 100644
--- a/tests/popover.ui
+++ b/tests/popover.ui
@@ -2,6 +2,18 @@
   <menu id="menu">
     <section>
       <item>
+        <attribute name="x-gtk-private-special">custom</attribute>
+        <attribute name="label">Grid</attribute>
+        <attribute name="action">top.set-view</attribute>
+      </item>
+      <item>
+        <attribute name="x-gtk-private-special">custom</attribute>
+        <attribute name="label">Zoom</attribute>
+        <attribute name="action">zoom</attribute>
+      </item>
+    </section>
+    <section>
+      <item>
         <attribute name="label">No action</attribute>
         <attribute name="action">action1</attribute>
       </item>
@@ -83,6 +95,11 @@
         <attribute name="label">Item 7</attribute>
         <attribute name="action">top.action7</attribute>
       </item>
+      <item>
+        <attribute name="x-gtk-private-special">custom</attribute>
+        <attribute name="label">Cut Copy Paste</attribute>
+        <attribute name="action">cutcopypaste</attribute>
+      </item>
       <submenu>
         <attribute name="label">Subsubmenu</attribute>
         <attribute name="icon">preferences-desktop-font</attribute>
diff --git a/tests/testpopover.c b/tests/testpopover.c
index f602daa..d06f3ad 100644
--- a/tests/testpopover.c
+++ b/tests/testpopover.c
@@ -19,9 +19,102 @@ static GActionEntry entries[] = {
   { "action7", activate, NULL, NULL, NULL },
   { "action8", activate, NULL, NULL, NULL },
   { "action9", activate, NULL, NULL, NULL },
-  { "action10", activate, NULL, NULL, NULL }
+  { "action10", activate, NULL, NULL, NULL },
+  { "set-view", NULL, "s", "'list'", NULL },
+  { "cut", activate, NULL, NULL, NULL },
+  { "copy", activate, NULL, NULL, NULL },
+  { "paste", activate, NULL, NULL, NULL }
 };
 
+static GtkWidget *
+pick_extra (GMenuItem *item, gpointer data)
+{
+  const gchar *id = NULL;
+  const gchar *action = NULL;
+
+  g_menu_item_get_attribute (item, "x-gtk-private-special", "&s", &id);
+  g_menu_item_get_attribute (item, "action", "&s", &action);
+  
+  if (g_strcmp0 (id, "custom") == 0)
+    {
+      if (g_strcmp0 (action, "top.set-view") == 0)
+        {
+          GtkWidget *box, *button, *image;
+          box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+          gtk_style_context_add_class (gtk_widget_get_style_context (box), "linked");
+
+          button = gtk_toggle_button_new ();
+          gtk_actionable_set_detailed_action_name (GTK_ACTIONABLE (button), "top.set-view::list");
+          image = gtk_image_new_from_icon_name ("view-list-symbolic", GTK_ICON_SIZE_MENU);
+          gtk_container_add (GTK_CONTAINER (button), image);
+          gtk_widget_set_hexpand (button, TRUE);
+          gtk_widget_set_halign (button, GTK_ALIGN_FILL);
+          gtk_container_add (GTK_CONTAINER (box), button);
+
+          button = gtk_toggle_button_new ();
+          gtk_actionable_set_detailed_action_name (GTK_ACTIONABLE (button), "top.set-view::grid");
+          image = gtk_image_new_from_icon_name ("view-grid-symbolic", GTK_ICON_SIZE_MENU);
+          gtk_container_add (GTK_CONTAINER (button), image);
+          gtk_widget_set_hexpand (button, TRUE);
+          gtk_widget_set_halign (button, GTK_ALIGN_FILL);
+          gtk_container_add (GTK_CONTAINER (box), button);
+
+          gtk_widget_show_all (box);
+          return box;
+        }
+      else if (g_strcmp0 (action, "zoom") == 0)
+        {
+          GtkWidget *scale;
+
+          scale = gtk_scale_new_with_range (GTK_ORIENTATION_HORIZONTAL, 0, 10, 1);
+          gtk_range_set_value (GTK_RANGE (scale), 5);
+
+          gtk_widget_set_margin_top (scale, 6);
+          gtk_widget_set_margin_bottom (scale, 6);
+          gtk_scale_set_draw_value (GTK_SCALE (scale), FALSE);
+          gtk_scale_set_has_origin (GTK_SCALE (scale), TRUE);
+          gtk_scale_add_mark (GTK_SCALE (scale), 0, GTK_POS_BOTTOM, NULL);
+          gtk_scale_add_mark (GTK_SCALE (scale), 5, GTK_POS_BOTTOM, NULL);
+          gtk_scale_add_mark (GTK_SCALE (scale), 10, GTK_POS_BOTTOM, NULL);
+          gtk_style_context_remove_class (gtk_widget_get_style_context (scale), 
GTK_STYLE_CLASS_SCALE_HAS_MARKS_BELOW);
+
+          gtk_widget_show (scale);
+          return scale;
+        }
+      else if (g_strcmp0 (action, "cutcopypaste") == 0)
+        {
+          GtkWidget *box, *button;
+          box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+          gtk_widget_set_margin_top (box, 6);
+          gtk_widget_set_margin_bottom (box, 6);
+          gtk_style_context_add_class (gtk_widget_get_style_context (box), "linked");
+
+          button = gtk_button_new_from_icon_name ("edit-cut-symbolic", GTK_ICON_SIZE_MENU);
+          gtk_actionable_set_detailed_action_name (GTK_ACTIONABLE (button), "top.cut");
+          gtk_widget_set_hexpand (button, TRUE);
+          gtk_widget_set_halign (button, GTK_ALIGN_FILL);
+          gtk_container_add (GTK_CONTAINER (box), button);
+
+          button = gtk_button_new_from_icon_name ("edit-copy-symbolic", GTK_ICON_SIZE_MENU);
+          gtk_actionable_set_detailed_action_name (GTK_ACTIONABLE (button), "top.copy");
+          gtk_widget_set_hexpand (button, TRUE);
+          gtk_widget_set_halign (button, GTK_ALIGN_FILL);
+          gtk_container_add (GTK_CONTAINER (box), button);
+
+          button = gtk_button_new_from_icon_name ("edit-paste-symbolic", GTK_ICON_SIZE_MENU);
+          gtk_actionable_set_detailed_action_name (GTK_ACTIONABLE (button), "top.paste");
+          gtk_widget_set_hexpand (button, TRUE);
+          gtk_widget_set_halign (button, GTK_ALIGN_FILL);
+          gtk_container_add (GTK_CONTAINER (box), button);
+
+          gtk_widget_show_all (box);
+          return box;
+        }
+    }
+
+  return NULL;
+}
+
 int main (int argc, char *argv[])
 {
   GtkWidget *win;
@@ -38,7 +131,6 @@ int main (int argc, char *argv[])
   GtkWidget *align;
 
   gtk_init (&argc, &argv);
-
   win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
   gtk_window_set_default_size (GTK_WINDOW (win), 400, 600);
   actions = g_simple_action_group_new ();
@@ -62,10 +154,16 @@ int main (int argc, char *argv[])
   model = (GMenuModel *)gtk_builder_get_object (builder, "menu");
 
   button = gtk_menu_button_new ();
+#if 0
   gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (button), model);
   gtk_menu_button_set_use_popover (GTK_MENU_BUTTON (button), TRUE);
 
   popover = GTK_WIDGET (gtk_menu_button_get_popover (GTK_MENU_BUTTON (button)));
+#else
+  popover = gtk_popover_new (NULL);
+  gtk_menu_button_set_popover (GTK_MENU_BUTTON (button), popover);
+  gtk_popover_bind_model_with_extra_widgets (GTK_POPOVER (popover), model, NULL, pick_extra, NULL);
+#endif
 
   g_object_set (button, "margin", 10, NULL);
   gtk_widget_set_halign (button, GTK_ALIGN_END);
@@ -125,7 +223,6 @@ int main (int argc, char *argv[])
   gtk_grid_attach (GTK_GRID (grid), label , 1, 5, 1, 1);
   gtk_grid_attach (GTK_GRID (grid), combo, 2, 5, 1, 1);
 
-
   gtk_widget_show_all (win);
 
   gtk_main ();


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