[gtk+/wip/attente/popup-at: 1/4] gtkmenu: add gtk_menu_popup_at_* ()



commit 0370262696e670d25c566d4d12658c4918855334
Author: William Hua <william hua canonical com>
Date:   Tue Jun 14 15:42:13 2016 -0400

    gtkmenu: add gtk_menu_popup_at_* ()
    
    Adds the following functions:
    
    gtk_menu_popup_at_rect ()
    gtk_menu_popup_at_widget ()
    gtk_menu_popup_at_pointer ()

 docs/reference/gtk/gtk3-sections.txt |    3 +
 gtk/gtkmenu.c                        |  468 ++++++++++++++++++++++++++++-----
 gtk/gtkmenu.h                        |   16 ++
 gtk/gtkmenuprivate.h                 |   18 ++
 4 files changed, 434 insertions(+), 71 deletions(-)
---
diff --git a/docs/reference/gtk/gtk3-sections.txt b/docs/reference/gtk/gtk3-sections.txt
index 0c072c6..853cb66 100644
--- a/docs/reference/gtk/gtk3-sections.txt
+++ b/docs/reference/gtk/gtk3-sections.txt
@@ -2241,6 +2241,9 @@ gtk_menu_new_from_model
 gtk_menu_set_screen
 gtk_menu_reorder_child
 gtk_menu_attach
+gtk_menu_popup_at_rect
+gtk_menu_popup_at_widget
+gtk_menu_popup_at_pointer
 gtk_menu_popup_for_device
 gtk_menu_popup
 gtk_menu_set_accel_group
diff --git a/gtk/gtkmenu.c b/gtk/gtkmenu.c
index 8531cf7..44fff75 100644
--- a/gtk/gtkmenu.c
+++ b/gtk/gtkmenu.c
@@ -195,7 +195,11 @@ enum {
   PROP_TEAROFF_STATE,
   PROP_TEAROFF_TITLE,
   PROP_MONITOR,
-  PROP_RESERVE_TOGGLE_SIZE
+  PROP_RESERVE_TOGGLE_SIZE,
+  PROP_ANCHOR_HINTS,
+  PROP_RECT_ANCHOR_DX,
+  PROP_RECT_ANCHOR_DY,
+  PROP_MENU_TYPE_HINT
 };
 
 enum {
@@ -693,6 +697,62 @@ gtk_menu_class_init (GtkMenuClass *class)
                                                          TRUE,
                                                          GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
 
+  g_object_class_install_property (gobject_class,
+                                   PROP_ANCHOR_HINTS,
+                                   g_param_spec_flags ("anchor-hints",
+                                                       P_("Anchor hints"),
+                                                       P_("Positioning hints for when the menu might fall 
off-screen"),
+                                                       GDK_TYPE_ANCHOR_HINTS,
+                                                       GDK_ANCHOR_FLIP |
+                                                       GDK_ANCHOR_SLIDE |
+                                                       GDK_ANCHOR_RESIZE,
+                                                       G_PARAM_READWRITE |
+                                                       G_PARAM_CONSTRUCT |
+                                                       G_PARAM_STATIC_NAME |
+                                                       G_PARAM_STATIC_NICK |
+                                                       G_PARAM_STATIC_BLURB));
+
+  g_object_class_install_property (gobject_class,
+                                   PROP_RECT_ANCHOR_DX,
+                                   g_param_spec_int ("rect-anchor-dx",
+                                                     P_("Rect anchor dx"),
+                                                     P_("Rect anchor horizontal offset"),
+                                                     G_MININT,
+                                                     G_MAXINT,
+                                                     0,
+                                                     G_PARAM_READWRITE |
+                                                     G_PARAM_CONSTRUCT |
+                                                     G_PARAM_STATIC_NAME |
+                                                     G_PARAM_STATIC_NICK |
+                                                     G_PARAM_STATIC_BLURB));
+
+  g_object_class_install_property (gobject_class,
+                                   PROP_RECT_ANCHOR_DY,
+                                   g_param_spec_int ("rect-anchor-dy",
+                                                     P_("Rect anchor dy"),
+                                                     P_("Rect anchor vertical offset"),
+                                                     G_MININT,
+                                                     G_MAXINT,
+                                                     0,
+                                                     G_PARAM_READWRITE |
+                                                     G_PARAM_CONSTRUCT |
+                                                     G_PARAM_STATIC_NAME |
+                                                     G_PARAM_STATIC_NICK |
+                                                     G_PARAM_STATIC_BLURB));
+
+  g_object_class_install_property (gobject_class,
+                                   PROP_MENU_TYPE_HINT,
+                                   g_param_spec_enum ("menu-type-hint",
+                                                      P_("Menu type hint"),
+                                                      P_("Menu window type hint"),
+                                                      GDK_TYPE_WINDOW_TYPE_HINT,
+                                                      GDK_WINDOW_TYPE_HINT_POPUP_MENU,
+                                                      G_PARAM_READWRITE |
+                                                      G_PARAM_CONSTRUCT |
+                                                      G_PARAM_STATIC_NAME |
+                                                      G_PARAM_STATIC_NICK |
+                                                      G_PARAM_STATIC_BLURB));
+
   /**
    * GtkMenu:horizontal-padding:
    *
@@ -964,6 +1024,18 @@ G_GNUC_END_IGNORE_DEPRECATIONS;
     case PROP_RESERVE_TOGGLE_SIZE:
       gtk_menu_set_reserve_toggle_size (menu, g_value_get_boolean (value));
       break;
+    case PROP_ANCHOR_HINTS:
+      menu->priv->anchor_hints = g_value_get_flags (value);
+      break;
+    case PROP_RECT_ANCHOR_DX:
+      menu->priv->rect_anchor_dx = g_value_get_int (value);
+      break;
+    case PROP_RECT_ANCHOR_DY:
+      menu->priv->rect_anchor_dy = g_value_get_int (value);
+      break;
+    case PROP_MENU_TYPE_HINT:
+      menu->priv->menu_type_hint = g_value_get_enum (value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -1008,6 +1080,18 @@ G_GNUC_END_IGNORE_DEPRECATIONS;
     case PROP_RESERVE_TOGGLE_SIZE:
       g_value_set_boolean (value, gtk_menu_get_reserve_toggle_size (menu));
       break;
+    case PROP_ANCHOR_HINTS:
+      g_value_set_flags (value, menu->priv->anchor_hints);
+      break;
+    case PROP_RECT_ANCHOR_DX:
+      g_value_set_int (value, menu->priv->rect_anchor_dx);
+      break;
+    case PROP_RECT_ANCHOR_DY:
+      g_value_set_int (value, menu->priv->rect_anchor_dy);
+      break;
+    case PROP_MENU_TYPE_HINT:
+      g_value_set_enum (value, menu->priv->menu_type_hint);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -1549,53 +1633,16 @@ associate_menu_grab_transfer_window (GtkMenu *menu)
   g_object_set_data (G_OBJECT (toplevel_window), I_("gdk-attached-grab-window"), transfer_window);
 }
 
-/**
- * gtk_menu_popup_for_device:
- * @menu: a #GtkMenu
- * @device: (allow-none): a #GdkDevice
- * @parent_menu_shell: (allow-none): the menu shell containing the triggering
- *     menu item, or %NULL
- * @parent_menu_item: (allow-none): the menu item whose activation triggered
- *     the popup, or %NULL
- * @func: (allow-none): a user supplied function used to position the menu,
- *     or %NULL
- * @data: (allow-none): user supplied data to be passed to @func
- * @destroy: (allow-none): destroy notify for @data
- * @button: the mouse button which was pressed to initiate the event
- * @activate_time: the time at which the activation event occurred
- *
- * Displays a menu and makes it available for selection.
- *
- * Applications can use this function to display context-sensitive menus,
- * and will typically supply %NULL for the @parent_menu_shell,
- * @parent_menu_item, @func, @data and @destroy parameters. The default
- * menu positioning function will position the menu at the current position
- * of @device (or its corresponding pointer).
- *
- * The @button parameter should be the mouse button pressed to initiate
- * the menu popup. If the menu popup was initiated by something other than
- * a mouse button press, such as a mouse button release or a keypress,
- * @button should be 0.
- *
- * The @activate_time parameter is used to conflict-resolve initiation of
- * concurrent requests for mouse/keyboard grab requests. To function
- * properly, this needs to be the time stamp of the user event (such as
- * a mouse click or key press) that caused the initiation of the popup.
- * Only if no such event is available, gtk_get_current_event_time() can
- * be used instead.
- *
- * Since: 3.0
- */
-void
-gtk_menu_popup_for_device (GtkMenu             *menu,
-                           GdkDevice           *device,
-                           GtkWidget           *parent_menu_shell,
-                           GtkWidget           *parent_menu_item,
-                           GtkMenuPositionFunc  func,
-                           gpointer             data,
-                           GDestroyNotify       destroy,
-                           guint                button,
-                           guint32              activate_time)
+static void
+gtk_menu_popup_internal (GtkMenu             *menu,
+                         GdkDevice           *device,
+                         GtkWidget           *parent_menu_shell,
+                         GtkWidget           *parent_menu_item,
+                         GtkMenuPositionFunc  func,
+                         gpointer             data,
+                         GDestroyNotify       destroy,
+                         guint                button,
+                         guint32              activate_time)
 {
   GtkMenuPrivate *priv = menu->priv;
   GtkWidget *widget;
@@ -1802,6 +1849,73 @@ gtk_menu_popup_for_device (GtkMenu             *menu,
 }
 
 /**
+ * gtk_menu_popup_for_device:
+ * @menu: a #GtkMenu
+ * @device: (allow-none): a #GdkDevice
+ * @parent_menu_shell: (allow-none): the menu shell containing the triggering
+ *     menu item, or %NULL
+ * @parent_menu_item: (allow-none): the menu item whose activation triggered
+ *     the popup, or %NULL
+ * @func: (allow-none): a user supplied function used to position the menu,
+ *     or %NULL
+ * @data: (allow-none): user supplied data to be passed to @func
+ * @destroy: (allow-none): destroy notify for @data
+ * @button: the mouse button which was pressed to initiate the event
+ * @activate_time: the time at which the activation event occurred
+ *
+ * Displays a menu and makes it available for selection.
+ *
+ * Applications can use this function to display context-sensitive menus,
+ * and will typically supply %NULL for the @parent_menu_shell,
+ * @parent_menu_item, @func, @data and @destroy parameters. The default
+ * menu positioning function will position the menu at the current position
+ * of @device (or its corresponding pointer).
+ *
+ * The @button parameter should be the mouse button pressed to initiate
+ * the menu popup. If the menu popup was initiated by something other than
+ * a mouse button press, such as a mouse button release or a keypress,
+ * @button should be 0.
+ *
+ * The @activate_time parameter is used to conflict-resolve initiation of
+ * concurrent requests for mouse/keyboard grab requests. To function
+ * properly, this needs to be the time stamp of the user event (such as
+ * a mouse click or key press) that caused the initiation of the popup.
+ * Only if no such event is available, gtk_get_current_event_time() can
+ * be used instead.
+ *
+ * Since: 3.0
+ */
+void
+gtk_menu_popup_for_device (GtkMenu             *menu,
+                           GdkDevice           *device,
+                           GtkWidget           *parent_menu_shell,
+                           GtkWidget           *parent_menu_item,
+                           GtkMenuPositionFunc  func,
+                           gpointer             data,
+                           GDestroyNotify       destroy,
+                           guint                button,
+                           guint32              activate_time)
+{
+  GtkMenuPrivate *priv;
+
+  g_return_if_fail (GTK_IS_MENU (menu));
+
+  priv = menu->priv;
+  priv->transient_for = NULL;
+  priv->widget = NULL;
+
+  gtk_menu_popup_internal (menu,
+                           device,
+                           parent_menu_shell,
+                           parent_menu_item,
+                           func,
+                           data,
+                           destroy,
+                           button,
+                           activate_time);
+}
+
+/**
  * gtk_menu_popup:
  * @menu: a #GtkMenu
  * @parent_menu_shell: (allow-none): the menu shell containing the
@@ -1853,6 +1967,241 @@ gtk_menu_popup (GtkMenu             *menu,
                              button, activate_time);
 }
 
+static GdkDevice *
+get_device_for_event (const GdkEvent *event)
+{
+  GdkDevice *device = NULL;
+  GdkSeat *seat = NULL;
+  GdkScreen *screen = NULL;
+  GdkDisplay *display = NULL;
+
+  device = gdk_event_get_device (event);
+
+  if (device)
+    return device;
+
+  seat = gdk_event_get_seat (event);
+
+  if (!seat)
+    {
+      screen = gdk_event_get_screen (event);
+
+      if (screen)
+        display = gdk_screen_get_display (screen);
+
+      if (!display)
+        display = gdk_display_get_default ();
+
+      if (display)
+        seat = gdk_display_get_default_seat (display);
+    }
+
+  return seat ? gdk_seat_get_pointer (seat) : NULL;
+}
+
+void
+gtk_menu_popup_at_rect (GtkMenu            *menu,
+                        GdkWindow          *transient_for,
+                        const GdkRectangle *rect,
+                        GdkGravity          rect_anchor,
+                        GdkGravity          menu_anchor,
+                        const GdkEvent     *trigger_event)
+{
+  GtkMenuPrivate *priv;
+  GdkEvent *current_event = NULL;
+  GdkDevice *device = NULL;
+  guint button = 0;
+  guint32 activate_time = GDK_CURRENT_TIME;
+
+  g_return_if_fail (GTK_IS_MENU (menu));
+  g_return_if_fail (GDK_IS_WINDOW (transient_for));
+  g_return_if_fail (rect);
+
+  priv = menu->priv;
+  priv->transient_for = transient_for;
+  priv->rect = *rect;
+  priv->widget = NULL;
+  priv->rect_anchor = rect_anchor;
+  priv->menu_anchor = menu_anchor;
+
+  if (!trigger_event)
+    {
+      current_event = gtk_get_current_event ();
+      trigger_event = current_event;
+    }
+
+  if (trigger_event)
+    {
+      device = get_device_for_event (trigger_event);
+      gdk_event_get_button (trigger_event, &button);
+      activate_time = gdk_event_get_time (trigger_event);
+    }
+  else
+    g_warning ("no trigger event for menu popup");
+
+  gtk_menu_popup_internal (menu,
+                           device,
+                           NULL,
+                           NULL,
+                           NULL,
+                           NULL,
+                           NULL,
+                           button,
+                           activate_time);
+
+  if (current_event)
+    gdk_event_free (current_event);
+}
+
+void
+gtk_menu_popup_at_widget (GtkMenu        *menu,
+                          GtkWidget      *widget,
+                          GdkGravity      widget_anchor,
+                          GdkGravity      menu_anchor,
+                          const GdkEvent *trigger_event)
+{
+  GtkMenuPrivate *priv;
+  GdkEvent *current_event = NULL;
+  GdkDevice *device = NULL;
+  guint button = 0;
+  guint32 activate_time = GDK_CURRENT_TIME;
+  GtkWidget *parent_menu_shell = NULL;
+  GtkWidget *parent_menu_item = NULL;
+
+  g_return_if_fail (GTK_IS_MENU (menu));
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+
+  priv = menu->priv;
+  priv->transient_for = NULL;
+  priv->widget = widget;
+  priv->rect_anchor = widget_anchor;
+  priv->menu_anchor = menu_anchor;
+
+  if (!trigger_event)
+    {
+      current_event = gtk_get_current_event ();
+      trigger_event = current_event;
+    }
+
+  if (trigger_event)
+    {
+      device = get_device_for_event (trigger_event);
+      gdk_event_get_button (trigger_event, &button);
+      activate_time = gdk_event_get_time (trigger_event);
+    }
+  else
+    g_warning ("no trigger event for menu popup");
+
+  if (GTK_IS_MENU_ITEM (priv->widget))
+    {
+      parent_menu_item = priv->widget;
+
+      if (GTK_IS_MENU_SHELL (gtk_widget_get_parent (parent_menu_item)))
+        parent_menu_shell = gtk_widget_get_parent (parent_menu_item);
+    }
+
+  gtk_menu_popup_internal (menu,
+                           device,
+                           parent_menu_shell,
+                           parent_menu_item,
+                           NULL,
+                           NULL,
+                           NULL,
+                           button,
+                           activate_time);
+
+  if (current_event)
+    gdk_event_free (current_event);
+}
+
+void
+gtk_menu_popup_at_pointer (GtkMenu        *menu,
+                           const GdkEvent *trigger_event)
+{
+  GdkEvent *current_event = NULL;
+  GdkWindow *transient_for = NULL;
+  GdkDevice *device = NULL;
+  GdkRectangle rect = { 0, 0, 1, 1 };
+
+  g_return_if_fail (GTK_IS_MENU (menu));
+
+  if (!trigger_event)
+    {
+      current_event = gtk_get_current_event ();
+      trigger_event = current_event;
+    }
+
+  if (trigger_event)
+    {
+      transient_for = gdk_event_get_window (trigger_event);
+
+      if (transient_for)
+        {
+          device = get_device_for_event (trigger_event);
+
+          if (device && gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
+            device = gdk_device_get_associated_device (device);
+
+          if (device)
+            gdk_window_get_device_position (transient_for, device, &rect.x, &rect.y, NULL);
+        }
+    }
+  else
+    g_warning ("no trigger event for menu popup");
+
+  gtk_menu_popup_at_rect (menu,
+                          transient_for,
+                          &rect,
+                          GDK_GRAVITY_SOUTH_EAST,
+                          GDK_GRAVITY_NORTH_WEST,
+                          trigger_event);
+
+  if (current_event)
+    gdk_event_free (current_event);
+}
+
+static void
+get_arrows_border (GtkMenu   *menu,
+                   GtkBorder *border)
+{
+  GtkMenuPrivate *priv = menu->priv;
+  gint top_arrow_height, bottom_arrow_height;
+
+  gtk_css_gadget_get_preferred_size (priv->top_arrow_gadget,
+                                     GTK_ORIENTATION_VERTICAL,
+                                     -1,
+                                     &top_arrow_height, NULL,
+                                     NULL, NULL);
+  gtk_css_gadget_get_preferred_size (priv->bottom_arrow_gadget,
+                                     GTK_ORIENTATION_VERTICAL,
+                                     -1,
+                                     &bottom_arrow_height, NULL,
+                                     NULL, NULL);
+
+  border->top = priv->upper_arrow_visible ? top_arrow_height : 0;
+  border->bottom = priv->lower_arrow_visible ? bottom_arrow_height : 0;
+  border->left = border->right = 0;
+}
+
+void
+gtk_menu_update_scroll_offset (GtkMenu            *menu,
+                               const GdkRectangle *flipped_rect,
+                               const GdkRectangle *slid_rect,
+                               gboolean            flipped_x,
+                               gboolean            flipped_y,
+                               gpointer            user_data)
+{
+  GtkBorder arrows_border;
+
+  g_return_if_fail (GTK_IS_MENU (menu));
+  g_return_if_fail (flipped_rect);
+  g_return_if_fail (slid_rect);
+
+  get_arrows_border (menu, &arrows_border);
+  menu->priv->scroll_offset = arrows_border.top + (slid_rect->y - flipped_rect->y);
+  gtk_menu_scroll_to (menu, menu->priv->scroll_offset);
+}
+
 /**
  * gtk_menu_popdown:
  * @menu: a #GtkMenu
@@ -2518,29 +2867,6 @@ gtk_menu_reorder_child (GtkMenu   *menu,
 }
 
 static void
-get_arrows_border (GtkMenu   *menu,
-                   GtkBorder *border)
-{
-  GtkMenuPrivate *priv = menu->priv;
-  gint top_arrow_height, bottom_arrow_height;
-
-  gtk_css_gadget_get_preferred_size (priv->top_arrow_gadget,
-                                     GTK_ORIENTATION_VERTICAL,
-                                     -1,
-                                     &top_arrow_height, NULL,
-                                     NULL, NULL);
-  gtk_css_gadget_get_preferred_size (priv->bottom_arrow_gadget,
-                                     GTK_ORIENTATION_VERTICAL,
-                                     -1,
-                                     &bottom_arrow_height, NULL,
-                                     NULL, NULL);
-
-  border->top = priv->upper_arrow_visible ? top_arrow_height : 0;
-  border->bottom = priv->lower_arrow_visible ? bottom_arrow_height : 0;
-  border->left = border->right = 0;
-}
-
-static void
 get_menu_padding (GtkWidget *widget,
                   GtkBorder *padding)
 {
diff --git a/gtk/gtkmenu.h b/gtk/gtkmenu.h
index 5b8cb14..c9f6157 100644
--- a/gtk/gtkmenu.h
+++ b/gtk/gtkmenu.h
@@ -152,6 +152,22 @@ void       gtk_menu_popup_for_device      (GtkMenu             *menu,
                                            GDestroyNotify       destroy,
                                            guint                button,
                                            guint32              activate_time);
+GDK_AVAILABLE_IN_3_22
+void       gtk_menu_popup_at_rect         (GtkMenu             *menu,
+                                           GdkWindow           *transient_for,
+                                           const GdkRectangle  *rect,
+                                           GdkGravity           rect_anchor,
+                                           GdkGravity           menu_anchor,
+                                           const GdkEvent      *trigger_event);
+GDK_AVAILABLE_IN_3_22
+void       gtk_menu_popup_at_widget       (GtkMenu             *menu,
+                                           GtkWidget           *widget,
+                                           GdkGravity           widget_anchor,
+                                           GdkGravity           menu_anchor,
+                                           const GdkEvent      *trigger_event);
+GDK_AVAILABLE_IN_3_22
+void       gtk_menu_popup_at_pointer      (GtkMenu             *menu,
+                                           const GdkEvent      *trigger_event);
 
 /* Position the menu according to its position function. Called
  * from gtkmenuitem.c when a menu-item changes its allocation
diff --git a/gtk/gtkmenuprivate.h b/gtk/gtkmenuprivate.h
index cb6ec25..87126b2 100644
--- a/gtk/gtkmenuprivate.h
+++ b/gtk/gtkmenuprivate.h
@@ -53,6 +53,16 @@ struct _GtkMenuPrivate
   gint                position_x;
   gint                position_y;
 
+  GdkWindow         *transient_for;
+  GdkRectangle       rect;
+  GtkWidget         *widget;
+  GdkGravity         rect_anchor;
+  GdkGravity         menu_anchor;
+  GdkAnchorHints     anchor_hints;
+  gint               rect_anchor_dx;
+  gint               rect_anchor_dy;
+  GdkWindowTypeHint  menu_type_hint;
+
   guint toggle_size;
   guint accel_size;
 
@@ -130,6 +140,14 @@ struct _GtkMenuPrivate
   gint initial_drag_offset;
 };
 
+G_GNUC_INTERNAL
+void gtk_menu_update_scroll_offset (GtkMenu            *menu,
+                                    const GdkRectangle *flipped_rect,
+                                    const GdkRectangle *slid_rect,
+                                    gboolean            flipped_x,
+                                    gboolean            flipped_y,
+                                    gpointer            user_data);
+
 G_END_DECLS
 
 #endif /* __GTK_MENU_PRIVATE_H__ */


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