[gtk/wip/matthiasc/context-menu: 1062/1062] Work towards generic menu popup



commit 6b15fa5267d0b065181a6c30499553df4b3725b6
Author: Matthias Clasen <mclasen redhat com>
Date:   Thu Apr 18 20:14:34 2019 +0000

    Work towards generic menu popup
    
    Repurpose the ::popup-menu signal for popping
    up context menus. Widgets such as GtkText or
    GtkLabel can override the class handler to update
    their actions before chaining up. Make GtkWidget
    handle context-menu popup automatically. I had
    to add a property to turn this off off, for complex
    cases like GtkText.

 gtk/gtklabel.c         | 188 ++++++++++++++++---------------------------------
 gtk/gtktext.c          |  66 ++---------------
 gtk/gtkwidget.c        | 116 ++++++++++++++++++++++++++++++
 gtk/gtkwidgetprivate.h |   5 ++
 4 files changed, 185 insertions(+), 190 deletions(-)
---
diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c
index 8cb67ed8bf..2a53191553 100644
--- a/gtk/gtklabel.c
+++ b/gtk/gtklabel.c
@@ -354,12 +354,12 @@ struct _GtkLabelSelectionInfo
 {
   gint selection_anchor;
   gint selection_end;
-  GtkWidget *popup_menu;
   GtkCssNode *selection_node;
   GdkContentProvider *provider;
 
   GList *links;
   GtkLabelLink *active_link;
+  GtkLabelLink *context_link;
 
   GtkGesture *drag_gesture;
   GtkGesture *click_gesture;
@@ -442,7 +442,6 @@ static gboolean gtk_label_focus         (GtkWidget         *widget,
 
 static void gtk_label_realize           (GtkWidget        *widget);
 static void gtk_label_unrealize         (GtkWidget        *widget);
-static void gtk_label_unmap             (GtkWidget        *widget);
 
 static void gtk_label_motion            (GtkEventControllerMotion *controller,
                                          double                    x,
@@ -535,8 +534,6 @@ static void gtk_label_move_cursor        (GtkLabel        *label,
                                          gboolean         extend_selection);
 static void gtk_label_copy_clipboard     (GtkLabel        *label);
 static void gtk_label_select_all         (GtkLabel        *label);
-static void gtk_label_do_popup           (GtkLabel        *label,
-                                         const GdkEvent  *event);
 static gint gtk_label_move_forward_word  (GtkLabel        *label,
                                          gint             start);
 static gint gtk_label_move_backward_word (GtkLabel        *label,
@@ -634,13 +631,12 @@ gtk_label_class_init (GtkLabelClass *class)
   widget_class->snapshot = gtk_label_snapshot;
   widget_class->realize = gtk_label_realize;
   widget_class->unrealize = gtk_label_unrealize;
-  widget_class->unmap = gtk_label_unmap;
   widget_class->root = gtk_label_root;
   widget_class->unroot = gtk_label_unroot;
   widget_class->mnemonic_activate = gtk_label_mnemonic_activate;
+  widget_class->popup_menu = gtk_label_popup_menu;
   widget_class->drag_data_get = gtk_label_drag_data_get;
   widget_class->grab_focus = gtk_label_grab_focus;
-  widget_class->popup_menu = gtk_label_popup_menu;
   widget_class->focus = gtk_label_focus;
   widget_class->get_request_mode = gtk_label_get_request_mode;
   widget_class->measure = gtk_label_measure;
@@ -1312,6 +1308,7 @@ gtk_label_init (GtkLabel *label)
 
   gtk_label_add_context_actions (label);
   menu = gtk_label_get_default_menu ();
+  g_object_set (label, "handle-context-menu", FALSE, NULL);
   gtk_widget_set_context_menu (GTK_WIDGET (label), menu);
   g_object_unref (menu);
 }
@@ -4105,24 +4102,6 @@ gtk_label_unrealize (GtkWidget *widget)
   GTK_WIDGET_CLASS (gtk_label_parent_class)->unrealize (widget);
 }
 
-static void
-gtk_label_unmap (GtkWidget *widget)
-{
-  GtkLabel *label = GTK_LABEL (widget);
-  GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
-
-  if (priv->select_info)
-    {
-      if (priv->select_info->popup_menu)
-        {
-          gtk_widget_destroy (priv->select_info->popup_menu);
-          priv->select_info->popup_menu = NULL;
-        }
-    }
-
-  GTK_WIDGET_CLASS (gtk_label_parent_class)->unmap (widget);
-}
-
 static gboolean
 get_layout_index (GtkLabel *label,
                   gint      x,
@@ -4461,7 +4440,7 @@ gtk_label_click_gesture_pressed (GtkGestureClick *gesture,
         {
           info->link_clicked = 1;
           update_link_state (label);
-          gtk_label_do_popup (label, event);
+          gtk_widget_popup_context_menu (widget);
           return;
         }
       else if (button == GDK_BUTTON_PRIMARY)
@@ -4484,7 +4463,7 @@ gtk_label_click_gesture_pressed (GtkGestureClick *gesture,
   info->select_words = FALSE;
 
   if (gdk_event_triggers_context_menu (event))
-    gtk_label_do_popup (label, event);
+    gtk_widget_popup_context_menu (GTK_WIDGET (label));
   else if (button == GDK_BUTTON_PRIMARY)
     {
       if (!gtk_widget_has_focus (widget))
@@ -5962,25 +5941,6 @@ gtk_label_select_all (GtkLabel *label)
   gtk_label_select_region_index (label, 0, strlen (priv->text));
 }
 
-static void
-popup_menu_detach (GtkWidget *attach_widget,
-                  GtkMenu   *menu)
-{
-  GtkLabel *label = GTK_LABEL (attach_widget);
-  GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
-
-  if (priv->select_info)
-    priv->select_info->popup_menu = NULL;
-}
-
-static gboolean
-gtk_label_popup_menu (GtkWidget *widget)
-{
-  gtk_label_do_popup (GTK_LABEL (widget), NULL);
-
-  return TRUE;
-}
-
 static void
 open_link_activated (GSimpleAction *action,
                      GVariant      *parameter,
@@ -5988,14 +5948,10 @@ open_link_activated (GSimpleAction *action,
 {
   GtkLabel *label = GTK_LABEL (user_data);
   GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
-  GtkLabelLink *link = NULL;
-  int pos = g_variant_get_int32 (parameter);
-
-  link = g_list_nth_data (priv->select_info->links, pos);
-  if (link == NULL)
-    return;
+  GtkLabelLink *link = priv->select_info->context_link;
 
-  emit_activate_link (label, link);
+  if (link)
+    emit_activate_link (label, link);
 }
 
 static void
@@ -6005,16 +5961,15 @@ copy_link_activated (GSimpleAction *action,
 {
   GtkLabel *label = GTK_LABEL (user_data);
   GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
-  GtkLabelLink *link = NULL;
-  int pos = g_variant_get_int32 (parameter);
-  GdkClipboard *clipboard;
+  GtkLabelLink *link = priv->select_info->context_link;
 
-  link = g_list_nth_data (priv->select_info->links, pos);
-  if (link == NULL)
-    return;
+  if (link)
+    {
+      GdkClipboard *clipboard;
 
-  clipboard = gtk_widget_get_clipboard (GTK_WIDGET (label));
-  gdk_clipboard_set_text (clipboard, link->uri);
+      clipboard = gtk_widget_get_clipboard (GTK_WIDGET (label));
+      gdk_clipboard_set_text (clipboard, link->uri);
+    }
 }
 
 static void
@@ -6049,19 +6004,39 @@ gtk_label_update_clipboard_actions (GtkLabel *label)
   g_simple_action_set_enabled (G_SIMPLE_ACTION (action), gtk_label_get_selectable (label));
 }
 
+static void
+gtk_label_update_link_actions (GtkLabel *label)
+{
+  GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
+  gboolean have_selection = FALSE;
+  GAction *action;
+  GtkLabelLink *link;
+
+  have_selection = priv->select_info->selection_anchor != priv->select_info->selection_end;
+  if (priv->select_info->link_clicked)
+    link = priv->select_info->active_link;
+  else
+    link = gtk_label_get_focus_link (label);
+
+  action = g_action_map_lookup_action (priv->context_actions, "open-link");
+  g_simple_action_set_enabled (G_SIMPLE_ACTION (action), !have_selection && link);
+  action = g_action_map_lookup_action (priv->context_actions, "copy-link");
+  g_simple_action_set_enabled (G_SIMPLE_ACTION (action), !have_selection && link);
+}
+
 static void
 gtk_label_add_context_actions (GtkLabel *label)
 {
   GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
 
   GActionEntry entries[] = {
-    { "open-link", open_link_activated, "i", NULL, NULL },
-    { "copy-link", copy_link_activated, "i", NULL, NULL },
     { "cut-clipboard", NULL, NULL, NULL, NULL },
     { "copy-clipboard", copy_clipboard_activated, NULL, NULL, NULL },
     { "paste-clipboard", NULL, NULL, NULL, NULL },
     { "delete-selection", NULL, NULL, NULL, NULL },
     { "select-all", select_all_activated, NULL, NULL, NULL },
+    { "open-link", open_link_activated, NULL, NULL, NULL },
+    { "copy-link", copy_link_activated, NULL, NULL, NULL },
   };
 
   GSimpleActionGroup *actions = g_simple_action_group_new ();
@@ -6081,6 +6056,10 @@ gtk_label_add_context_actions (GtkLabel *label)
   g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
   action = g_action_map_lookup_action (G_ACTION_MAP (actions), "select-all");
   g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
+  action = g_action_map_lookup_action (G_ACTION_MAP (actions), "open-link");
+  g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
+  action = g_action_map_lookup_action (G_ACTION_MAP (actions), "copy-link");
+  g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
 
   gtk_widget_insert_action_group (GTK_WIDGET (label), "context", G_ACTION_GROUP (actions));
 }
@@ -6089,6 +6068,7 @@ static GMenuModel *
 gtk_label_get_default_menu (void)
 {
   GMenu *menu, *section;
+  GMenuItem *item;
 
   menu = g_menu_new ();
 
@@ -6105,89 +6085,39 @@ gtk_label_get_default_menu (void)
   g_menu_append_section (menu, NULL, G_MENU_MODEL (section));
   g_object_unref (section);
 
-  return G_MENU_MODEL (menu);
-}
-
-static GMenuModel *
-get_link_context_menu (GtkLabel     *label,
-                       GtkLabelLink *link)
-{
-  GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
-  GMenu *menu;
-  GMenuItem *item;
-  int index;
-
-  index = g_list_index (priv->select_info->links, link);
-
-  menu = g_menu_new ();
+  section = g_menu_new ();
   item = g_menu_item_new (_("_Open Link"), "context.open-link");
-  g_menu_item_set_attribute (item, "target", "i", index);
-  g_menu_append_item (menu, item);
+  g_menu_item_set_attribute (item, "hidden-when", "s", "action-disabled");
+  g_menu_append_item (section, item);
   g_object_unref (item);
-
-  item = g_menu_item_new (_("_Copy Link Address"), "context.copy-link");
-  g_menu_item_set_attribute (item, "target", "i", index);
-  g_menu_append_item (menu, item);
+  item = g_menu_item_new (_("Copy _Link Address"), "context.copy-link");
+  g_menu_item_set_attribute (item, "hidden-when", "s", "action-disabled");
+  g_menu_append_item (section, item);
   g_object_unref (item);
+  g_menu_append_section (menu, NULL, G_MENU_MODEL (section));
+  g_object_unref (section);
 
   return G_MENU_MODEL (menu);
 }
 
-static void
-gtk_label_do_popup (GtkLabel       *label,
-                    const GdkEvent *event)
+static gboolean
+gtk_label_popup_menu (GtkWidget *widget)
 {
+  GtkLabel *label = GTK_LABEL (widget);
   GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
-  GtkWidget *menu;
-  GMenuModel *model;
-  GtkLabelLink *link;
-  gboolean have_selection;
 
   if (!priv->select_info)
-    return;
+    return FALSE;
 
   gtk_label_update_clipboard_actions (label);
+  gtk_label_update_link_actions (label);
 
-  if (priv->select_info->popup_menu)
-    gtk_widget_destroy (priv->select_info->popup_menu);
-
-  have_selection = priv->select_info->selection_anchor != priv->select_info->selection_end;
-  if (event)
-    {
-      if (priv->select_info->link_clicked)
-        link = priv->select_info->active_link;
-      else
-        link = NULL;
-    }
-  else
-    link = gtk_label_get_focus_link (label);
-
-  if (!have_selection && link)
-    model = get_link_context_menu (label, link);
-  else
-    model = g_object_ref (gtk_widget_get_context_menu (GTK_WIDGET (label)));
-
-  priv->select_info->popup_menu = menu = gtk_menu_new_from_model (model);
-
-  g_object_unref (model);
-
-  g_object_set_data (G_OBJECT (menu), "link", link);
-
-  gtk_style_context_add_class (gtk_widget_get_style_context (menu), GTK_STYLE_CLASS_CONTEXT_MENU);
-  gtk_menu_attach_to_widget (GTK_MENU (menu), GTK_WIDGET (label), popup_menu_detach);
-
-  if (event && gdk_event_triggers_context_menu (event))
-    gtk_menu_popup_at_pointer (GTK_MENU (menu), event);
+  if (priv->select_info->link_clicked)
+    priv->select_info->context_link = priv->select_info->active_link;
   else
-    {
-      gtk_menu_popup_at_widget (GTK_MENU (menu),
-                                GTK_WIDGET (label),
-                                GDK_GRAVITY_SOUTH,
-                                GDK_GRAVITY_NORTH_WEST,
-                                event);
+    priv->select_info->context_link = gtk_label_get_focus_link (label);
 
-      gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
-    }
+  return GTK_WIDGET_CLASS (gtk_label_parent_class)->popup_menu (widget);
 }
 
 static void
diff --git a/gtk/gtktext.c b/gtk/gtktext.c
index 532f2f91ff..6bcdc024dc 100644
--- a/gtk/gtktext.c
+++ b/gtk/gtktext.c
@@ -146,7 +146,6 @@ struct _GtkTextPrivate
 {
   GtkEntryBuffer *buffer;
   GtkIMContext   *im_context;
-  GtkWidget      *popup_menu;
 
   int             text_baseline;
 
@@ -514,8 +513,6 @@ static void         gtk_text_paste                    (GtkText        *self,
                                                        GdkClipboard   *clipboard);
 static void         gtk_text_update_primary_selection (GtkText        *self);
 static void         gtk_text_schedule_im_reset        (GtkText        *self);
-static void         gtk_text_do_popup                 (GtkText        *self,
-                                                       const GdkEvent *event);
 static gboolean     gtk_text_mnemonic_activate        (GtkWidget      *widget,
                                                        gboolean        group_cycling);
 static void         gtk_text_check_cursor_blink       (GtkText        *self);
@@ -1703,6 +1700,7 @@ gtk_text_init (GtkText *self)
   gtk_text_add_context_actions (self);
 
   menu = gtk_text_get_default_menu ();
+  g_object_set (self, "handle-context-menu", FALSE, NULL);
   gtk_widget_set_context_menu (GTK_WIDGET (self), menu);
   g_object_unref (menu);
 }
@@ -2017,12 +2015,6 @@ gtk_text_unrealize (GtkWidget *widget)
   if (gdk_clipboard_get_content (clipboard) == priv->selection_content)
     gdk_clipboard_set_content (clipboard, NULL);
 
-  if (priv->popup_menu)
-    {
-      gtk_widget_destroy (priv->popup_menu);
-      priv->popup_menu = NULL;
-    }
-
   GTK_WIDGET_CLASS (gtk_text_parent_class)->unrealize (widget);
 }
 
@@ -2444,7 +2436,8 @@ gtk_text_click_gesture_pressed (GtkGestureClick *gesture,
 
   if (gdk_event_triggers_context_menu (event))
     {
-      gtk_text_do_popup (self, event);
+      gtk_text_update_clipboard_actions (self);
+      gtk_widget_popup_context_menu (widget);
     }
   else if (n_press == 1 && button == GDK_BUTTON_MIDDLE &&
            get_middle_click_paste (self))
@@ -5768,60 +5761,11 @@ gtk_text_mnemonic_activate (GtkWidget *widget,
   return GDK_EVENT_STOP;
 }
 
-static void
-popup_menu_detach (GtkWidget *attach_widget,
-                   GtkMenu   *menu)
-{
-  GtkText *self_attach = GTK_TEXT (attach_widget);
-  GtkTextPrivate *priv_attach = gtk_text_get_instance_private (self_attach);
-
-  priv_attach->popup_menu = NULL;
-}
-
-static void
-gtk_text_do_popup (GtkText        *self,
-                   const GdkEvent *event)
-{
-  GtkTextPrivate *priv = gtk_text_get_instance_private (self);
-  GMenuModel *model;
-  GtkWidget *menu;
-  GdkEvent *trigger_event;
-
-  if (!gtk_widget_get_realized (GTK_WIDGET (self)))
-    return;
-
-  gtk_text_update_clipboard_actions (self);
-
-  if (priv->popup_menu)
-    gtk_widget_destroy (priv->popup_menu);
-
-  model = gtk_widget_get_context_menu (GTK_WIDGET (self));
-  priv->popup_menu = menu = gtk_menu_new_from_model (model);
-
-  gtk_style_context_add_class (gtk_widget_get_style_context (menu), GTK_STYLE_CLASS_CONTEXT_MENU);
-  gtk_menu_attach_to_widget (GTK_MENU (menu), GTK_WIDGET (self), popup_menu_detach);
-
-  trigger_event = event ? gdk_event_copy (event) : gtk_get_current_event ();
-  if (trigger_event && gdk_event_triggers_context_menu (trigger_event))
-    gtk_menu_popup_at_pointer (GTK_MENU (menu), trigger_event);
-  else
-    {
-      gtk_menu_popup_at_widget (GTK_MENU (menu),
-                                GTK_WIDGET (self),
-                                GDK_GRAVITY_SOUTH_EAST,
-                                GDK_GRAVITY_NORTH_WEST,
-                                trigger_event);
-      gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
-    }
- 
-  g_clear_object (&trigger_event);
-}
-
 static gboolean
 gtk_text_popup_menu (GtkWidget *widget)
 {
-  gtk_text_do_popup (GTK_TEXT (widget), NULL);
-  return GDK_EVENT_STOP;
+  gtk_text_update_clipboard_actions (GTK_TEXT (widget));
+  return GTK_WIDGET_CLASS (gtk_text_parent_class)->popup_menu (widget);
 }
 
 static void
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index 01e6a1d447..65ea45b2c8 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -73,6 +73,7 @@
 #include "gtkwindowgroup.h"
 #include "gtkwindowprivate.h"
 #include "gtknativeprivate.h"
+#include "gtkgestureclick.h"
 
 #include "a11y/gtkwidgetaccessible.h"
 #include "inspector/window.h"
@@ -576,6 +577,7 @@ enum {
   PROP_CSS_NAME,
   PROP_LAYOUT_MANAGER,
   PROP_CONTEXT_MENU,
+  PROP_HANDLE_CONTEXT_MENU,
   NUM_PROPERTIES
 };
 
@@ -665,6 +667,7 @@ static void             gtk_widget_real_measure                 (GtkWidget
 static void             gtk_widget_real_state_flags_changed     (GtkWidget        *widget,
                                                                  GtkStateFlags     old_state);
 static AtkObject*      gtk_widget_real_get_accessible          (GtkWidget        *widget);
+static gboolean         gtk_widget_real_popup_menu              (GtkWidget        *widget);
 static void            gtk_widget_accessible_interface_init    (AtkImplementorIface *iface);
 static AtkObject*      gtk_widget_ref_accessible               (AtkImplementor *implementor);
 static gboolean         gtk_widget_real_can_activate_accel      (GtkWidget *widget,
@@ -861,6 +864,61 @@ gtk_widget_real_unroot (GtkWidget *widget)
   gtk_widget_forall (widget, (GtkCallback) gtk_widget_unroot, NULL);
 }
 
+static void
+popup_menu_detach (GtkWidget *widget,
+                   GtkMenu   *menu)
+{
+  GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget);
+
+  priv->popup_menu = NULL;
+}
+
+static gboolean
+gtk_widget_real_popup_menu (GtkWidget *widget)
+{
+  GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget);
+  GtkWidget *menu;
+  const GdkEvent *event;
+
+  if (!gtk_widget_get_realized (widget))
+    return FALSE;
+
+  if (!priv->context_menu)
+    return FALSE;
+
+  if (priv->popup_menu)
+    gtk_widget_destroy (priv->popup_menu);
+
+  priv->popup_menu = menu = gtk_menu_new_from_model (priv->context_menu);
+  gtk_style_context_add_class (gtk_widget_get_style_context (menu), GTK_STYLE_CLASS_CONTEXT_MENU);
+  gtk_menu_attach_to_widget (GTK_MENU (menu), widget, popup_menu_detach);
+
+  event = gtk_get_current_event ();
+  if (event && gdk_event_triggers_context_menu (event))
+    gtk_menu_popup_at_pointer (GTK_MENU (menu), event);
+  else
+    {
+      gtk_menu_popup_at_widget (GTK_MENU (menu),
+                                widget,
+                                GDK_GRAVITY_SOUTH_EAST,
+                                GDK_GRAVITY_NORTH_WEST,
+                                event);
+      gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
+    }
+
+  return TRUE;
+}
+
+gboolean
+gtk_widget_popup_context_menu (GtkWidget *widget)
+{
+  gboolean handled;
+
+  g_signal_emit (widget, widget_signals[POPUP_MENU], 0, &handled);
+
+  return handled;
+}
+
 static void
 gtk_widget_class_init (GtkWidgetClass *klass)
 {
@@ -923,6 +981,7 @@ gtk_widget_class_init (GtkWidgetClass *klass)
   klass->can_activate_accel = gtk_widget_real_can_activate_accel;
   klass->query_tooltip = gtk_widget_real_query_tooltip;
   klass->style_updated = gtk_widget_real_style_updated;
+  klass->popup_menu = gtk_widget_real_popup_menu;
 
   /* Accessibility support */
   klass->priv->accessible_type = GTK_TYPE_ACCESSIBLE;
@@ -1368,6 +1427,13 @@ gtk_widget_class_init (GtkWidgetClass *klass)
                          G_TYPE_MENU_MODEL,
                          GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
 
+  widget_props[PROP_HANDLE_CONTEXT_MENU] =
+      g_param_spec_boolean ("handle-context-menu",
+                            P_("Handle Context Menu"),
+                            P_("Whether the context menu should be handled automatically"),
+                            TRUE,
+                            GTK_PARAM_READWRITE);
+
   g_object_class_install_properties (gobject_class, NUM_PROPERTIES, widget_props);
 
   /**
@@ -2310,6 +2376,9 @@ gtk_widget_set_property (GObject         *object,
     case PROP_CONTEXT_MENU:
       gtk_widget_set_context_menu (widget, g_value_get_object (value));
       break;
+    case PROP_HANDLE_CONTEXT_MENU:
+      priv->handle_context_menu = g_value_get_boolean (value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -2459,6 +2528,9 @@ gtk_widget_get_property (GObject         *object,
     case PROP_CONTEXT_MENU:
       g_value_set_object (value, gtk_widget_get_context_menu (widget));
       break;
+    case PROP_HANDLE_CONTEXT_MENU:
+      g_value_set_boolean (value, priv->handle_context_menu);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -2805,6 +2877,7 @@ gtk_widget_init (GTypeInstance *instance, gpointer g_class)
   priv->highlight_resize = FALSE;
 #endif
   priv->can_target = TRUE;
+  priv->handle_context_menu = TRUE;
 
   switch (_gtk_widget_get_direction (widget))
     {
@@ -8225,6 +8298,12 @@ gtk_widget_real_unrealize (GtkWidget *widget)
   priv->realized = FALSE;
 
   g_clear_object (&priv->surface);
+
+  if (priv->popup_menu)
+    {
+      gtk_widget_destroy (priv->popup_menu);
+      priv->popup_menu = NULL;
+    }
 }
 
 void
@@ -13426,6 +13505,23 @@ gtk_widget_get_layout_manager (GtkWidget *widget)
   return priv->layout_manager;
 }
 
+static void
+popup_controller_pressed (GtkGestureClick *gesture,
+                          int              n_press,
+                          double           widget_x,
+                          double           widget_y,
+                          GtkWidget       *widget)
+{
+  GdkEvent *event;
+
+  event = gtk_get_current_event ();
+  if (event && gdk_event_triggers_context_menu (event))
+    {
+      gboolean handled;
+      g_signal_emit (widget, widget_signals[POPUP_MENU], 0, &handled);
+    }
+}
+
 /**
  * gtk_widget_set_context_menu:
  * @widget: a #GtkWidget
@@ -13445,6 +13541,26 @@ gtk_widget_set_context_menu (GtkWidget  *widget,
 
   if (g_set_object (&priv->context_menu, menu))
     g_object_notify_by_pspec (G_OBJECT (widget), widget_props[PROP_CONTEXT_MENU]);
+
+  if (priv->context_menu && priv->handle_context_menu)
+    {
+      if (!priv->popup_controller)
+        {
+          priv->popup_controller = GTK_EVENT_CONTROLLER (gtk_gesture_click_new ());
+          gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (priv->popup_controller), 0);
+          g_signal_connect (priv->popup_controller, "pressed",
+                            G_CALLBACK (popup_controller_pressed), widget);
+          gtk_widget_add_controller (widget, priv->popup_controller);
+        }
+    }
+  else
+    {
+      if (priv->popup_controller)
+        {
+          gtk_widget_remove_controller (widget, priv->popup_controller);
+          priv->popup_controller = NULL;
+        }
+    }
 }
 
 /**
diff --git a/gtk/gtkwidgetprivate.h b/gtk/gtkwidgetprivate.h
index cf44b5582f..46cca274d9 100644
--- a/gtk/gtkwidgetprivate.h
+++ b/gtk/gtkwidgetprivate.h
@@ -111,6 +111,8 @@ struct _GtkWidgetPrivate
   guint   halign              : 4;
   guint   valign              : 4;
 
+  guint   handle_context_menu : 1;
+
   GtkOverflow overflow;
   guint8 alpha;
   guint8 user_alpha;
@@ -207,6 +209,8 @@ struct _GtkWidgetPrivate
   GdkCursor *cursor;
 
   GMenuModel *context_menu;
+  GtkWidget *popup_menu;
+  GtkEventController *popup_controller;
 };
 
 void          gtk_widget_root               (GtkWidget *widget);
@@ -359,6 +363,7 @@ void              gtk_widget_cancel_event_sequence         (GtkWidget
 gboolean          gtk_widget_run_controllers               (GtkWidget           *widget,
                                                             const GdkEvent      *event,
                                                             GtkPropagationPhase  phase);
+gboolean         gtk_widget_popup_context_menu             (GtkWidget           *widget);
 
 
 guint             gtk_widget_add_surface_transform_changed_callback (GtkWidget                          
*widget,


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