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



commit 4309f049b04d84a09cd9170e526234db09dcf11d
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/gtkmarshalers.list               |    1 +
 gtk/gtkmenu.c                        |  640 ++++++++++++++++++++++++++++++----
 gtk/gtkmenu.h                        |   16 +
 gtk/gtkmenuprivate.h                 |   18 +
 5 files changed, 607 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/gtkmarshalers.list b/gtk/gtkmarshalers.list
index c6f12a3..32c0ded 100644
--- a/gtk/gtkmarshalers.list
+++ b/gtk/gtkmarshalers.list
@@ -106,6 +106,7 @@ VOID:POINTER
 VOID:POINTER,INT
 VOID:POINTER,BOOLEAN
 VOID:POINTER,POINTER,BOOLEAN
+VOID:POINTER,POINTER,BOOLEAN,BOOLEAN
 VOID:POINTER,POINTER,POINTER
 VOID:POINTER,UINT
 VOID:STRING
diff --git a/gtk/gtkmenu.c b/gtk/gtkmenu.c
index 8531cf7..3d20b96 100644
--- a/gtk/gtkmenu.c
+++ b/gtk/gtkmenu.c
@@ -183,6 +183,7 @@ typedef struct
 
 enum {
   MOVE_SCROLL,
+  POPPED_UP,
   LAST_SIGNAL
 };
 
@@ -195,7 +196,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 {
@@ -562,6 +567,49 @@ gtk_menu_class_init (GtkMenuClass *class)
                                 GTK_TYPE_SCROLL_TYPE);
 
   /**
+   * GtkMenu::popped-up:
+   * @menu: the #GtkMenu that popped up
+   * @flipped_rect: the position of @menu after any possible flipping
+   * @slid_rect: the position of @menu after any possible sliding
+   * @flipped_x: %TRUE if the anchors were flipped horizontally
+   * @flipped_y: %TRUE if the anchors were flipped vertically
+   *
+   * Emitted when the position of @menu is finalized after being popped up
+   * using gtk_menu_popup_at_rect (), gtk_menu_popup_at_widget (), or
+   * gtk_menu_popup_at_pointer ().
+   *
+   * @menu might be flipped over the anchor rectangle in order to keep it
+   * on-screen, in which case @flipped_x and @flipped_y will be set to %TRUE
+   * accordingly.
+   *
+   * @flipped_rect is the ideal position of @window after any possible
+   * flipping, but before any possible sliding. @slid_rect is @flipped_rect,
+   * but possibly translated in the case that flipping is still ineffective in
+   * keeping @window on-screen.
+   *
+   * See gtk_menu_popup_at_rect (), gtk_menu_popup_at_widget (),
+   * gtk_menu_popup_at_pointer (), #GtkMenu:anchor-hints,
+   * #GtkMenu:rect-anchor-dx, #GtkMenu:rect-anchor-dy, and
+   * #GtkMenu:menu-type-hint.
+   *
+   * Since: 3.22
+   */
+  menu_signals[POPPED_UP] =
+    g_signal_new_class_handler (I_("popped-up"),
+                                G_OBJECT_CLASS_TYPE (gobject_class),
+                                G_SIGNAL_RUN_FIRST,
+                                NULL,
+                                NULL,
+                                NULL,
+                                _gtk_marshal_VOID__POINTER_POINTER_BOOLEAN_BOOLEAN,
+                                G_TYPE_NONE,
+                                4,
+                                G_TYPE_POINTER,
+                                G_TYPE_POINTER,
+                                G_TYPE_BOOLEAN,
+                                G_TYPE_BOOLEAN);
+
+  /**
    * GtkMenu:active:
    *
    * The index of the currently selected menu item, or -1 if no
@@ -694,6 +742,113 @@ gtk_menu_class_init (GtkMenuClass *class)
                                                          GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
 
   /**
+   * GtkMenu:anchor-hints:
+   *
+   * Positioning hints for aligning the menu relative to a rectangle.
+   *
+   * These hints determine how the menu should be positioned in the case that
+   * the menu would fall off-screen if placed in its ideal position.
+   *
+   * For example, %GDK_ANCHOR_FLIP_X will replace %GDK_GRAVITY_NORTH_WEST with
+   * %GDK_GRAVITY_NORTH_EAST and vice versa if the menu extends beyond the left
+   * or right edges of the monitor.
+   *
+   * See gtk_menu_popup_at_rect (), gtk_menu_popup_at_widget (),
+   * gtk_menu_popup_at_pointer (), #GtkMenu:rect-anchor-dx,
+   * #GtkMenu:rect-anchor-dy, #GtkMenu:menu-type-hint, and #GtkMenu::popped-up.
+   *
+   * Since: 3.22
+   */
+  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));
+
+  /**
+   * GtkMenu:rect-anchor-dx:
+   *
+   * Horizontal offset to apply to the rectangle or widget anchor.
+   *
+   * See gtk_menu_popup_at_rect (), gtk_menu_popup_at_widget (),
+   * gtk_menu_popup_at_pointer (), #GtkMenu:anchor-hints, #GtkMenu:rect-anchor-dy,
+   * #GtkMenu:menu-type-hint, and #GtkMenu::popped-up.
+   *
+   * Since: 3.22
+   */
+  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));
+
+  /**
+   * GtkMenu:rect-anchor-dy:
+   *
+   * Vertical offset to apply to the rectangle or widget anchor.
+   *
+   * See gtk_menu_popup_at_rect (), gtk_menu_popup_at_widget (),
+   * gtk_menu_popup_at_pointer (), #GtkMenu:anchor-hints,
+   * #GtkMenu:rect-anchor-dx, #GtkMenu:menu-type-hint, and #GtkMenu::popped-up.
+   *
+   * Since: 3.22
+   */
+  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));
+
+  /**
+   * GtkMenu:menu-type-hint:
+   *
+   * The #GdkWindowTypeHint to use for the menu's #GdkWindow.
+   *
+   * See gtk_menu_popup_at_rect (), gtk_menu_popup_at_widget (),
+   * gtk_menu_popup_at_pointer (), #GtkMenu:anchor-hints,
+   * #GtkMenu:rect-anchor-dx, #GtkMenu:rect-anchor-dy, and #GtkMenu::popped-up.
+   *
+   * Since: 3.22
+   */
+  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:
    *
    * Extra space at the left and right edges of the menu.
@@ -964,6 +1119,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 +1175,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 +1728,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 +1944,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 +2062,318 @@ 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;
+}
+
+/**
+ * gtk_menu_popup_at_rect:
+ * @menu: the #GtkMenu to pop up
+ * @transient_for: (not nullable): the #GdkWindow @rect is relative to
+ * @rect: (not nullable): the #GdkRectangle to align @menu with
+ * @rect_anchor: the point on @rect to align with @menu's anchor point
+ * @menu_anchor: the point on @menu to align with @rect's anchor point
+ * @trigger_event: the #GdkEvent that initiated this request
+ *
+ * Displays @menu and makes it available for selection.
+ *
+ * @menu will be positioned at @rect, aligning their anchor points. @rect is
+ * relative to the top-left corner of @transient_for. @rect_anchor and
+ * @menu_anchor determine anchor points on @rect and @menu to pin together.
+ * @rect's anchor point can optionally be offset by #GtkMenu:rect-anchor-dx and
+ * #GtkMenu:rect-anchor-dy.
+ *
+ * Other properties that influence the behaviour of this function are
+ * #GtkMenu:anchor-hints and #GtkMenu:menu-type-hint. Connect to the
+ * #GtkMenu::popped-up signal to find out how it was actually positioned.
+ *
+ * Since: 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)
+{
+  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);
+}
+
+/**
+ * gtk_menu_popup_at_widget:
+ * @menu: the #GtkMenu to pop up
+ * @widget: (not nullable): the #GtkWidget to align @menu with
+ * @widget_anchor: the point on @widget to align with @menu's anchor point
+ * @menu_anchor: the point on @menu to align with @widget's anchor point
+ * @trigger_event: the #GdkEvent that initiated this request
+ *
+ * Displays @menu and makes it available for selection.
+ *
+ * @menu will be positioned at @widget, aligning their anchor points.
+ * @widget_anchor and @menu_anchor determine anchor points on @widget and @menu
+ * to pin together. @widget's anchor point can optionally be offset by
+ * #GtkMenu:rect-anchor-dx and #GtkMenu:rect-anchor-dy.
+ *
+ * Other properties that influence the behaviour of this function are
+ * #GtkMenu:anchor-hints and #GtkMenu:menu-type-hint. Connect to the
+ * #GtkMenu::popped-up signal to find out how it was actually positioned.
+ *
+ * Since: 3.22
+ */
+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);
+}
+
+/**
+ * gtk_menu_popup_at_pointer:
+ * @menu: the #GtkMenu to pop up
+ * @trigger_event: the #GdkEvent that initiated this request
+ *
+ * Displays @menu and makes it available for selection.
+ *
+ * @menu will be positioned at the pointer associated with @trigger_event.
+ *
+ * Properties that influence the behaviour of this function are
+ * #GtkMenu:anchor-hints, #GtkMenu:rect-anchor-dx, #GtkMenu:rect-anchor-dy, and
+ * #GtkMenu:menu-type-hint. Connect to the #GtkMenu::popped-up signal to find
+ * out how it was actually positioned.
+ *
+ * Since: 3.22
+ */
+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;
+}
+
+/**
+ * gtk_menu_update_scroll_offset:
+ * @menu: the #GtkMenu that popped up
+ * @flipped_rect: (not nullable): the position of @menu after any possible
+ *                flipping
+ * @slid_rect: (not nullable): the position of @menu after any possible sliding
+ * @flipped_x: %TRUE if the anchors were flipped horizontally
+ * @flipped_y: %TRUE if the anchors were flipped vertically
+ * @user_data: user data
+ *
+ * Updates the scroll offset of @menu based on the amount of sliding done while
+ * positioning @menu. Connect this to the #GtkMenu::popped-up signal to keep the
+ * contents of the menu vertically aligned with their ideal position, for combo
+ * boxes for example.
+ *
+ * Since: 3.22
+ */
+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 +3039,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]