[gtk+/wip/attente/popup-at: 1/5] gdkwindow: add gdk_window_move_to_rect ()



commit 5de231d91af4feae2401d20058cfde867b5ee21e
Author: William Hua <william hua canonical com>
Date:   Wed Jun 15 11:00:38 2016 -0400

    gdkwindow: add gdk_window_move_to_rect ()

 docs/reference/gdk/gdk3-sections.txt |    1 +
 gdk/gdk-private.c                    |    3 +-
 gdk/gdk-private.h                    |   18 +++
 gdk/gdkmarshalers.list               |    1 +
 gdk/gdkwindow.c                      |   99 +++++++++++++
 gdk/gdkwindow.h                      |   37 +++++
 gdk/gdkwindowimpl.c                  |  252 ++++++++++++++++++++++++++++++++++
 gdk/gdkwindowimpl.h                  |    8 +
 8 files changed, 418 insertions(+), 1 deletions(-)
---
diff --git a/docs/reference/gdk/gdk3-sections.txt b/docs/reference/gdk/gdk3-sections.txt
index b0fa61f..c057ba9 100644
--- a/docs/reference/gdk/gdk3-sections.txt
+++ b/docs/reference/gdk/gdk3-sections.txt
@@ -341,6 +341,7 @@ GdkWindowWindowClass
 GdkWindowHints
 GdkGeometry
 GdkGravity
+GdkAnchorHints
 GdkWindowEdge
 GdkWindowTypeHint
 GdkWindowAttr
diff --git a/gdk/gdk-private.c b/gdk/gdk-private.c
index 7b62cc3..37a4ee6 100644
--- a/gdk/gdk-private.c
+++ b/gdk/gdk-private.c
@@ -16,7 +16,8 @@ gdk__private__ (void)
     gdk_display_get_rendering_mode,
     gdk_display_set_rendering_mode,
     gdk_display_get_debug_updates,
-    gdk_display_set_debug_updates
+    gdk_display_set_debug_updates,
+    gdk_window_move_to_rect
   };
 
   return &table;
diff --git a/gdk/gdk-private.h b/gdk/gdk-private.h
index 474172d..32e3aaa 100644
--- a/gdk/gdk-private.h
+++ b/gdk/gdk-private.h
@@ -31,6 +31,15 @@ gboolean         gdk_display_get_debug_updates (GdkDisplay *display);
 void             gdk_display_set_debug_updates (GdkDisplay *display,
                                                 gboolean    debug_updates);
 
+void            gdk_window_move_to_rect         (GdkWindow          *window,
+                                                 GdkWindow          *transient_for,
+                                                 const GdkRectangle *rect,
+                                                 GdkGravity          rect_anchor,
+                                                 GdkGravity          window_anchor,
+                                                 GdkAnchorHints      anchor_hints,
+                                                 gint                rect_anchor_dx,
+                                                 gint                rect_anchor_dy);
+
 typedef struct {
   /* add all private functions here, initialize them in gdk-private.c */
   gboolean (* gdk_device_grab_info) (GdkDisplay  *display,
@@ -56,6 +65,15 @@ typedef struct {
   gboolean         (* gdk_display_get_debug_updates) (GdkDisplay *display);
   void             (* gdk_display_set_debug_updates) (GdkDisplay *display,
                                                       gboolean    debug_updates);
+
+  void (* gdk_window_move_to_rect) (GdkWindow          *window,
+                                    GdkWindow          *transient_for,
+                                    const GdkRectangle *rect,
+                                    GdkGravity          rect_anchor,
+                                    GdkGravity          window_anchor,
+                                    GdkAnchorHints      anchor_hints,
+                                    gint                rect_anchor_dx,
+                                    gint                rect_anchor_dy);
 } GdkPrivateVTable;
 
 GDK_AVAILABLE_IN_ALL
diff --git a/gdk/gdkmarshalers.list b/gdk/gdkmarshalers.list
index cb42499..adc37b9 100644
--- a/gdk/gdkmarshalers.list
+++ b/gdk/gdkmarshalers.list
@@ -5,3 +5,4 @@ OBJECT:VOID
 OBJECT:DOUBLE,DOUBLE
 BOXED:INT,INT
 VOID:DOUBLE,DOUBLE,POINTER,POINTER
+VOID:POINTER,POINTER,BOOLEAN,BOOLEAN
diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c
index d5ffc53..cd05ac0 100644
--- a/gdk/gdkwindow.c
+++ b/gdk/gdkwindow.c
@@ -148,6 +148,7 @@ enum {
   TO_EMBEDDER,
   FROM_EMBEDDER,
   CREATE_SURFACE,
+  MOVED_TO_RECT,
   LAST_SIGNAL
 };
 
@@ -476,6 +477,43 @@ gdk_window_class_init (GdkWindowClass *klass)
                   2,
                   G_TYPE_INT,
                   G_TYPE_INT);
+
+  /**
+   * GdkWindow::moved-to-rect:
+   * @window: the #GdkWindow that moved
+   * @flipped_rect: the position of @window after any possible flipping
+   * @slid_rect: the position of @window 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 @window is finalized after being moved to a
+   * destination rectangle.
+   *
+   * @window might be flipped over the destination 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.
+   *
+   * Since: 3.22
+   */
+  signals[MOVED_TO_RECT] =
+    g_signal_new (g_intern_static_string ("moved-to-rect"),
+                  G_OBJECT_CLASS_TYPE (object_class),
+                  G_SIGNAL_RUN_FIRST,
+                  0,
+                  NULL,
+                  NULL,
+                  _gdk_marshal_VOID__POINTER_POINTER_BOOLEAN_BOOLEAN,
+                  G_TYPE_NONE,
+                  4,
+                  G_TYPE_POINTER,
+                  G_TYPE_POINTER,
+                  G_TYPE_BOOLEAN,
+                  G_TYPE_BOOLEAN);
 }
 
 static void
@@ -6137,6 +6175,67 @@ gdk_window_move_resize (GdkWindow *window,
   gdk_window_move_resize_internal (window, TRUE, x, y, width, height);
 }
 
+/**
+ * gdk_window_move_to_rect:
+ * @window: the #GdkWindow to move
+ * @transient_for: (not nullable): the #GdkWindow @rect is relative to
+ * @rect: (not nullable): the destination #GdkRectangle to align @window with
+ * @rect_anchor: the point on @rect to align with @window's anchor point
+ * @window_anchor: the point on @window to align with @rect's anchor point
+ * @anchor_hints: positioning hints to use when limited on space
+ * @rect_anchor_dx: horizontal offset to shift @rect's anchor point
+ * @rect_anchor_dy: vertical offset to shift @rect's anchor point
+ *
+ * Moves @window to @rect, aligning their anchor points.
+ *
+ * @rect is relative to the top-left corner of @transient_for. @rect_anchor and
+ * @window_anchor determine anchor points on @rect and @window to pin together.
+ * @rect's anchor point can optionally be offset by @rect_anchor_dx and
+ * @rect_anchor_dy.
+ *
+ * @anchor_hints determines how @window will be moved if the anchor points cause
+ * it to move off-screen. For example, %GDK_ANCHOR_FLIP_X will replace
+ * %GDK_GRAVITY_NORTH_WEST with %GDK_GRAVITY_NORTH_EAST and vice versa if
+ * @window extends beyond the left or right edges of the monitor.
+ *
+ * Since: 3.22
+ */
+void
+gdk_window_move_to_rect (GdkWindow          *window,
+                         GdkWindow          *transient_for,
+                         const GdkRectangle *rect,
+                         GdkGravity          rect_anchor,
+                         GdkGravity          window_anchor,
+                         GdkAnchorHints      anchor_hints,
+                         gint                rect_anchor_dx,
+                         gint                rect_anchor_dy)
+{
+  GdkWindowImplClass *impl_class;
+  GdkRectangle transformed_rect;
+
+  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_WINDOW (transient_for));
+  g_return_if_fail (rect);
+
+  transformed_rect = *rect;
+
+  while (!gdk_window_has_native (transient_for) && gdk_window_get_effective_parent (transient_for))
+    {
+      transformed_rect.x += transient_for->x;
+      transformed_rect.y += transient_for->y;
+      transient_for = gdk_window_get_effective_parent (transient_for);
+    }
+
+  impl_class = GDK_WINDOW_IMPL_GET_CLASS (window->impl);
+  impl_class->move_to_rect (window,
+                            transient_for,
+                            &transformed_rect,
+                            rect_anchor,
+                            window_anchor,
+                            anchor_hints,
+                            rect_anchor_dx,
+                            rect_anchor_dy);
+}
 
 /**
  * gdk_window_scroll:
diff --git a/gdk/gdkwindow.h b/gdk/gdkwindow.h
index 4d98e07..626bd86 100644
--- a/gdk/gdkwindow.h
+++ b/gdk/gdkwindow.h
@@ -245,6 +245,43 @@ typedef enum
   GDK_GRAVITY_STATIC
 } GdkGravity;
 
+/**
+ * GdkAnchorHints:
+ * @GDK_ANCHOR_FLIP_X: allow flipping anchors horizontally
+ * @GDK_ANCHOR_FLIP_Y: allow flipping anchors vertically
+ * @GDK_ANCHOR_SLIDE_X: allow sliding window horizontally
+ * @GDK_ANCHOR_SLIDE_Y: allow sliding window vertically
+ * @GDK_ANCHOR_RESIZE_X: allow resizing window horizontally
+ * @GDK_ANCHOR_RESIZE_Y: allow resizing window vertically
+ * @GDK_ANCHOR_FLIP: allow flipping anchors on both axes
+ * @GDK_ANCHOR_SLIDE: allow sliding window on both axes
+ * @GDK_ANCHOR_RESIZE: allow resizing window on both axes
+ *
+ * Positioning hints for aligning a window relative to a rectangle.
+ *
+ * These hints determine how the window should be positioned in the case that
+ * the window would fall off-screen if placed in its ideal position. The
+ * default implementation first attempts to flip the anchors if allowed, then
+ * attempts to slide the window on-screen if allowed.
+ *
+ * For example, %GDK_ANCHOR_FLIP_X will replace %GDK_GRAVITY_NORTH_WEST with
+ * %GDK_GRAVITY_NORTH_EAST and vice versa if the window extends beyond the left
+ * or right edges of the monitor.
+ *
+ * Since: 3.22
+ */
+typedef enum
+{
+  GDK_ANCHOR_FLIP_X   = 1 << 0,
+  GDK_ANCHOR_FLIP_Y   = 1 << 1,
+  GDK_ANCHOR_SLIDE_X  = 1 << 2,
+  GDK_ANCHOR_SLIDE_Y  = 1 << 3,
+  GDK_ANCHOR_RESIZE_X = 1 << 4,
+  GDK_ANCHOR_RESIZE_Y = 1 << 5,
+  GDK_ANCHOR_FLIP     = GDK_ANCHOR_FLIP_X | GDK_ANCHOR_FLIP_Y,
+  GDK_ANCHOR_SLIDE    = GDK_ANCHOR_SLIDE_X | GDK_ANCHOR_SLIDE_Y,
+  GDK_ANCHOR_RESIZE   = GDK_ANCHOR_RESIZE_X | GDK_ANCHOR_RESIZE_Y
+} GdkAnchorHints;
 
 /**
  * GdkWindowEdge:
diff --git a/gdk/gdkwindowimpl.c b/gdk/gdkwindowimpl.c
index b7ec4ed..bdd96f5 100644
--- a/gdk/gdkwindowimpl.c
+++ b/gdk/gdkwindowimpl.c
@@ -39,6 +39,257 @@ gdk_window_impl_beep (GdkWindow *window)
   return FALSE;
 }
 
+static GdkDisplay *
+get_display_for_window (GdkWindow *primary,
+                        GdkWindow *secondary)
+{
+  GdkDisplay *display = gdk_window_get_display (primary);
+
+  if (display)
+    return display;
+
+  display = gdk_window_get_display (secondary);
+
+  if (display)
+    return display;
+
+  return gdk_display_get_default ();
+}
+
+static GdkMonitor *
+get_monitor_for_rect (GdkDisplay         *display,
+                      const GdkRectangle *rect)
+{
+  gint biggest_area = G_MININT;
+  GdkMonitor *best_monitor = NULL;
+  GdkMonitor *monitor;
+  GdkRectangle workarea;
+  GdkRectangle intersection;
+  gint x;
+  gint y;
+  gint i;
+
+  for (i = 0; i < gdk_display_get_n_monitors (display); i++)
+    {
+      monitor = gdk_display_get_monitor (display, i);
+      gdk_monitor_get_workarea (monitor, &workarea);
+
+      if (gdk_rectangle_intersect (&workarea, rect, &intersection))
+        {
+          if (intersection.width * intersection.height > biggest_area)
+            {
+              biggest_area = intersection.width * intersection.height;
+              best_monitor = monitor;
+            }
+        }
+    }
+
+  if (best_monitor)
+    return best_monitor;
+
+  x = rect->x + rect->width / 2;
+  y = rect->y + rect->height / 2;
+
+  return gdk_display_get_monitor_at_point (display, x, y);
+}
+
+static gint
+get_anchor_x (GdkGravity anchor)
+{
+  switch (anchor)
+    {
+    case GDK_GRAVITY_STATIC:
+    case GDK_GRAVITY_NORTH_WEST:
+    case GDK_GRAVITY_WEST:
+    case GDK_GRAVITY_SOUTH_WEST:
+      return -1;
+
+    default:
+    case GDK_GRAVITY_NORTH:
+    case GDK_GRAVITY_CENTER:
+    case GDK_GRAVITY_SOUTH:
+      return 0;
+
+    case GDK_GRAVITY_NORTH_EAST:
+    case GDK_GRAVITY_EAST:
+    case GDK_GRAVITY_SOUTH_EAST:
+      return 1;
+    }
+}
+
+static gint
+get_anchor_y (GdkGravity anchor)
+{
+  switch (anchor)
+    {
+    case GDK_GRAVITY_STATIC:
+    case GDK_GRAVITY_NORTH_WEST:
+    case GDK_GRAVITY_NORTH:
+    case GDK_GRAVITY_NORTH_EAST:
+      return -1;
+
+    default:
+    case GDK_GRAVITY_WEST:
+    case GDK_GRAVITY_CENTER:
+    case GDK_GRAVITY_EAST:
+      return 0;
+
+    case GDK_GRAVITY_SOUTH_WEST:
+    case GDK_GRAVITY_SOUTH:
+    case GDK_GRAVITY_SOUTH_EAST:
+      return 1;
+    }
+}
+
+static gint
+choose_position (gint      bounds_x,
+                 gint      bounds_width,
+                 gint      rect_x,
+                 gint      rect_width,
+                 gint      window_width,
+                 gint      rect_anchor,
+                 gint      window_anchor,
+                 gint      rect_anchor_dx,
+                 gboolean  flip,
+                 gboolean *flipped)
+{
+  gint x;
+
+  *flipped = FALSE;
+  x = rect_x + (1 + rect_anchor) * rect_width / 2 + rect_anchor_dx - (1 + window_anchor) * window_width / 2;
+
+  if (!flip || (x >= bounds_x && x + window_width <= bounds_x + bounds_width))
+    return x;
+
+  *flipped = TRUE;
+  x = rect_x + (1 - rect_anchor) * rect_width / 2 - rect_anchor_dx - (1 - window_anchor) * window_width / 2;
+
+  if (x >= bounds_x && x + window_width <= bounds_x + bounds_width)
+    return x;
+
+  *flipped = FALSE;
+  return rect_x + (1 + rect_anchor) * rect_width / 2 + rect_anchor_dx - (1 + window_anchor) * window_width / 
2;
+}
+
+static void
+gdk_window_impl_move_to_rect (GdkWindow          *window,
+                              GdkWindow          *transient_for,
+                              const GdkRectangle *rect,
+                              GdkGravity          rect_anchor,
+                              GdkGravity          window_anchor,
+                              GdkAnchorHints      anchor_hints,
+                              gint                rect_anchor_dx,
+                              gint                rect_anchor_dy)
+{
+  GdkDisplay *display;
+  GdkMonitor *monitor;
+  GdkRectangle bounds;
+  GdkRectangle root_rect = *rect;
+  GdkRectangle flipped_rect;
+  GdkRectangle slid_rect;
+  gboolean flipped_x;
+  gboolean flipped_y;
+
+  gdk_window_get_root_coords (transient_for,
+                              root_rect.x,
+                              root_rect.y,
+                              &root_rect.x,
+                              &root_rect.y);
+
+  display = get_display_for_window (window, transient_for);
+  monitor = get_monitor_for_rect (display, &root_rect);
+  gdk_monitor_get_workarea (monitor, &bounds);
+
+  flipped_rect.width = window->width - window->shadow_left - window->shadow_right;
+  flipped_rect.height = window->height - window->shadow_top - window->shadow_bottom;
+  flipped_rect.x = choose_position (bounds.x,
+                                    bounds.width,
+                                    root_rect.x,
+                                    root_rect.width,
+                                    flipped_rect.width,
+                                    get_anchor_x (rect_anchor),
+                                    get_anchor_x (window_anchor),
+                                    rect_anchor_dx,
+                                    anchor_hints & GDK_ANCHOR_FLIP_X,
+                                    &flipped_x);
+  flipped_rect.y = choose_position (bounds.y,
+                                    bounds.height,
+                                    root_rect.y,
+                                    root_rect.height,
+                                    flipped_rect.height,
+                                    get_anchor_y (rect_anchor),
+                                    get_anchor_y (window_anchor),
+                                    rect_anchor_dy,
+                                    anchor_hints & GDK_ANCHOR_FLIP_Y,
+                                    &flipped_y);
+
+  slid_rect = flipped_rect;
+
+  if (anchor_hints & GDK_ANCHOR_SLIDE_X)
+    {
+      if (slid_rect.x + slid_rect.width > bounds.x + bounds.width)
+        slid_rect.x = bounds.x + bounds.width - slid_rect.width;
+
+      if (slid_rect.x < bounds.x)
+        slid_rect.x = bounds.x;
+    }
+
+  if (anchor_hints & GDK_ANCHOR_SLIDE_Y)
+    {
+      if (slid_rect.y + slid_rect.height > bounds.y + bounds.height)
+        slid_rect.y = bounds.y + bounds.height - slid_rect.height;
+
+      if (slid_rect.y < bounds.y)
+        slid_rect.y = bounds.y;
+    }
+
+  if (anchor_hints & GDK_ANCHOR_RESIZE_X)
+    {
+      if (slid_rect.x < bounds.x)
+        {
+          slid_rect.width -= bounds.x - slid_rect.x;
+          slid_rect.x = bounds.x;
+        }
+
+      if (slid_rect.x + slid_rect.width > bounds.x + bounds.width)
+        slid_rect.width = bounds.x + bounds.width - slid_rect.x;
+    }
+
+  if (anchor_hints & GDK_ANCHOR_RESIZE_Y)
+    {
+      if (slid_rect.y < bounds.y)
+        {
+          slid_rect.height -= bounds.y - slid_rect.y;
+          slid_rect.y = bounds.y;
+        }
+
+      if (slid_rect.y + slid_rect.height > bounds.y + bounds.height)
+        slid_rect.height = bounds.y + bounds.height - slid_rect.y;
+    }
+
+  flipped_rect.x -= window->shadow_left;
+  flipped_rect.y -= window->shadow_top;
+  flipped_rect.width += window->shadow_left + window->shadow_right;
+  flipped_rect.height += window->shadow_top + window->shadow_bottom;
+
+  slid_rect.x -= window->shadow_left;
+  slid_rect.y -= window->shadow_top;
+  slid_rect.width += window->shadow_left + window->shadow_right;
+  slid_rect.height += window->shadow_top + window->shadow_bottom;
+
+  if (slid_rect.width != window->width || slid_rect.height != window->height)
+    gdk_window_move_resize (window, slid_rect.x, slid_rect.y, slid_rect.width, slid_rect.height);
+  else
+    gdk_window_move (window, slid_rect.x, slid_rect.y);
+
+  g_signal_emit_by_name (window,
+                         "moved-to-rect",
+                         &flipped_rect,
+                         &slid_rect,
+                         flipped_x,
+                         flipped_y);
+}
+
 static void
 gdk_window_impl_process_updates_recurse (GdkWindow      *window,
                                          cairo_region_t *region)
@@ -50,6 +301,7 @@ static void
 gdk_window_impl_class_init (GdkWindowImplClass *impl_class)
 {
   impl_class->beep = gdk_window_impl_beep;
+  impl_class->move_to_rect = gdk_window_impl_move_to_rect;
   impl_class->process_updates_recurse = gdk_window_impl_process_updates_recurse;
 }
 
diff --git a/gdk/gdkwindowimpl.h b/gdk/gdkwindowimpl.h
index 4ccdc67..61915e9 100644
--- a/gdk/gdkwindowimpl.h
+++ b/gdk/gdkwindowimpl.h
@@ -75,6 +75,14 @@ struct _GdkWindowImplClass
                                          gint             y,
                                          gint             width,
                                          gint             height);
+  void         (* move_to_rect)         (GdkWindow       *window,
+                                         GdkWindow       *transient_for,
+                                         const GdkRectangle *rect,
+                                         GdkGravity       rect_anchor,
+                                         GdkGravity       window_anchor,
+                                         GdkAnchorHints   anchor_hints,
+                                         gint             rect_anchor_dx,
+                                         gint             rect_anchor_dy);
   void         (* set_background)       (GdkWindow       *window,
                                          cairo_pattern_t *pattern);
 


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