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



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

    gdkwindow: add gdk_window_move_to_rect ()
    
    https://bugzilla.gnome.org/show_bug.cgi?id=756579

 docs/reference/gdk/gdk3-sections.txt |    1 +
 gdk/gdk-private.c                    |    3 +-
 gdk/gdk-private.h                    |   18 +++
 gdk/gdkmarshalers.list               |    1 +
 gdk/gdkwindow.c                      |  106 ++++++++++++++
 gdk/gdkwindow.h                      |   42 ++++++
 gdk/gdkwindowimpl.c                  |  254 ++++++++++++++++++++++++++++++++++
 gdk/gdkwindowimpl.h                  |    8 +
 8 files changed, 432 insertions(+), 1 deletions(-)
---
diff --git a/docs/reference/gdk/gdk3-sections.txt b/docs/reference/gdk/gdk3-sections.txt
index c09c246..b1d389b 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..991d87b 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          *rect_window,
+                                                 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          *rect_window,
+                                    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 04fb29c..83a8cc9 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,45 @@ gdk_window_class_init (GdkWindowClass *klass)
                   2,
                   G_TYPE_INT,
                   G_TYPE_INT);
+
+  /**
+   * GdkWindow::moved-to-rect:
+   * @window: the #GdkWindow that moved
+   * @flipped_rect: (not nullable): the position of @window after any possible
+   *                flipping
+   * @final_rect: (not nullable): the final position of @window
+   * @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. This signal is only emitted if the backend supports
+   * it.
+   *
+   * @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. @final_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 +6177,72 @@ 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
+ * @rect_window: (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 @window, i.e. @rect's anchor
+ *                  point
+ * @rect_anchor_dy: vertical offset to shift @window, i.e. @rect's anchor point
+ *
+ * Moves @window to @rect, aligning their anchor points.
+ *
+ * @rect is relative to the top-left corner of @rect_window. @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.
+ *
+ * If the backend supports it, the #GdkWindow::moved-to-rect signal will be
+ * emitted once the position is finalized.
+ *
+ * Since: 3.22
+ */
+void
+gdk_window_move_to_rect (GdkWindow          *window,
+                         GdkWindow          *rect_window,
+                         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 (rect_window));
+  g_return_if_fail (gdk_window_get_display (window) == gdk_window_get_display (rect_window));
+  g_return_if_fail (rect);
+
+  transformed_rect = *rect;
+
+  while (!gdk_window_has_native (rect_window) && gdk_window_get_effective_parent (rect_window))
+    {
+      transformed_rect.x += rect_window->x;
+      transformed_rect.y += rect_window->y;
+      rect_window = gdk_window_get_effective_parent (rect_window);
+    }
+
+  impl_class = GDK_WINDOW_IMPL_GET_CLASS (window->impl);
+  impl_class->move_to_rect (window,
+                            rect_window,
+                            &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..2a52e0d 100644
--- a/gdk/gdkwindow.h
+++ b/gdk/gdkwindow.h
@@ -245,6 +245,48 @@ 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.
+ *
+ * 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.
+ *
+ * If %GDK_ANCHOR_SLIDE_X is set, the window can be shifted horizontally to fit
+ * on-screen. If %GDK_ANCHOR_RESIZE_X is set, the window can be shrunken
+ * horizontally to fit.
+ *
+ * In general, when multiple flags are set, flipping should take precedence over
+ * sliding, which should take precedence over resizing.
+ *
+ * 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..773ea8b 100644
--- a/gdk/gdkwindowimpl.c
+++ b/gdk/gdkwindowimpl.c
@@ -39,6 +39,259 @@ 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;
+
+  g_warning ("no display for window, using default");
+  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_sign (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_sign (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
+maybe_flip_position (gint      bounds_pos,
+                     gint      bounds_size,
+                     gint      rect_pos,
+                     gint      rect_size,
+                     gint      window_size,
+                     gint      rect_sign,
+                     gint      window_sign,
+                     gint      offset,
+                     gboolean  flip,
+                     gboolean *flipped)
+{
+  gint primary;
+  gint secondary;
+
+  *flipped = FALSE;
+  primary = rect_pos + (1 + rect_sign) * rect_size / 2 + offset - (1 + window_sign) * window_size / 2;
+
+  if (!flip || (primary >= bounds_pos && primary + window_size <= bounds_pos + bounds_size))
+    return primary;
+
+  *flipped = TRUE;
+  secondary = rect_pos + (1 - rect_sign) * rect_size / 2 - offset - (1 - window_sign) * window_size / 2;
+
+  if (secondary >= bounds_pos && secondary + window_size <= bounds_pos + bounds_size)
+    return secondary;
+
+  *flipped = FALSE;
+  return primary;
+}
+
+static void
+gdk_window_impl_move_to_rect (GdkWindow          *window,
+                              GdkWindow          *rect_window,
+                              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 final_rect;
+  gboolean flipped_x;
+  gboolean flipped_y;
+
+  gdk_window_get_root_coords (rect_window,
+                              root_rect.x,
+                              root_rect.y,
+                              &root_rect.x,
+                              &root_rect.y);
+
+  display = get_display_for_window (window, rect_window);
+  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 = maybe_flip_position (bounds.x,
+                                        bounds.width,
+                                        root_rect.x,
+                                        root_rect.width,
+                                        flipped_rect.width,
+                                        get_anchor_x_sign (rect_anchor),
+                                        get_anchor_x_sign (window_anchor),
+                                        rect_anchor_dx,
+                                        anchor_hints & GDK_ANCHOR_FLIP_X,
+                                        &flipped_x);
+  flipped_rect.y = maybe_flip_position (bounds.y,
+                                        bounds.height,
+                                        root_rect.y,
+                                        root_rect.height,
+                                        flipped_rect.height,
+                                        get_anchor_y_sign (rect_anchor),
+                                        get_anchor_y_sign (window_anchor),
+                                        rect_anchor_dy,
+                                        anchor_hints & GDK_ANCHOR_FLIP_Y,
+                                        &flipped_y);
+
+  final_rect = flipped_rect;
+
+  if (anchor_hints & GDK_ANCHOR_SLIDE_X)
+    {
+      if (final_rect.x + final_rect.width > bounds.x + bounds.width)
+        final_rect.x = bounds.x + bounds.width - final_rect.width;
+
+      if (final_rect.x < bounds.x)
+        final_rect.x = bounds.x;
+    }
+
+  if (anchor_hints & GDK_ANCHOR_SLIDE_Y)
+    {
+      if (final_rect.y + final_rect.height > bounds.y + bounds.height)
+        final_rect.y = bounds.y + bounds.height - final_rect.height;
+
+      if (final_rect.y < bounds.y)
+        final_rect.y = bounds.y;
+    }
+
+  if (anchor_hints & GDK_ANCHOR_RESIZE_X)
+    {
+      if (final_rect.x < bounds.x)
+        {
+          final_rect.width -= bounds.x - final_rect.x;
+          final_rect.x = bounds.x;
+        }
+
+      if (final_rect.x + final_rect.width > bounds.x + bounds.width)
+        final_rect.width = bounds.x + bounds.width - final_rect.x;
+    }
+
+  if (anchor_hints & GDK_ANCHOR_RESIZE_Y)
+    {
+      if (final_rect.y < bounds.y)
+        {
+          final_rect.height -= bounds.y - final_rect.y;
+          final_rect.y = bounds.y;
+        }
+
+      if (final_rect.y + final_rect.height > bounds.y + bounds.height)
+        final_rect.height = bounds.y + bounds.height - final_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;
+
+  final_rect.x -= window->shadow_left;
+  final_rect.y -= window->shadow_top;
+  final_rect.width += window->shadow_left + window->shadow_right;
+  final_rect.height += window->shadow_top + window->shadow_bottom;
+
+  if (final_rect.width != window->width || final_rect.height != window->height)
+    gdk_window_move_resize (window, final_rect.x, final_rect.y, final_rect.width, final_rect.height);
+  else
+    gdk_window_move (window, final_rect.x, final_rect.y);
+
+  g_signal_emit_by_name (window,
+                         "moved-to-rect",
+                         &flipped_rect,
+                         &final_rect,
+                         flipped_x,
+                         flipped_y);
+}
+
 static void
 gdk_window_impl_process_updates_recurse (GdkWindow      *window,
                                          cairo_region_t *region)
@@ -50,6 +303,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..9dce6de 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       *rect_window,
+                                         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]