summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWilliam Hua <william.hua@canonical.com>2016-06-15 11:00:38 -0400
committerWilliam Hua <william.hua@canonical.com>2016-07-19 09:38:54 -0400
commitb3a530cb727c504923d047347b982a9fcd1490b7 (patch)
tree32d40b7af0db27bb1a10dff0a6a3282efb39e73f
parent48108c401ec474a3841b93745e563b4645a3f0d0 (diff)
downloadgtk+-b3a530cb727c504923d047347b982a9fcd1490b7.tar.gz
gdkwindow: add gdk_window_move_to_rect ()
https://bugzilla.gnome.org/show_bug.cgi?id=756579
-rw-r--r--docs/reference/gdk/gdk3-sections.txt1
-rw-r--r--gdk/gdk-private.c3
-rw-r--r--gdk/gdk-private.h16
-rw-r--r--gdk/gdkmarshalers.list1
-rw-r--r--gdk/gdkwindow.c95
-rw-r--r--gdk/gdkwindow.h43
-rw-r--r--gdk/gdkwindowimpl.c253
-rw-r--r--gdk/gdkwindowimpl.h7
8 files changed, 418 insertions, 1 deletions
diff --git a/docs/reference/gdk/gdk3-sections.txt b/docs/reference/gdk/gdk3-sections.txt
index c09c2461eb..b1d389b16d 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 7b62cc3208..37a4ee60c3 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 474172dfdc..69d126638f 100644
--- a/gdk/gdk-private.h
+++ b/gdk/gdk-private.h
@@ -31,6 +31,14 @@ 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,
+ 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 +64,14 @@ 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,
+ 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 cb42499721..adc37b985c 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 42085a1da0..aa28e4aa95 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,46 @@ gdk_window_class_init (GdkWindowClass *klass)
2,
G_TYPE_INT,
G_TYPE_INT);
+
+ /**
+ * GdkWindow::moved-to-rect:
+ * @window: the #GdkWindow that moved
+ * @flipped_rect: (nullable): the position of @window after any possible
+ * flipping or %NULL if the backend can't obtain it
+ * @final_rect: (nullable): the final position of @window or %NULL if the
+ * backend can't obtain it
+ * @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. @final_rect is @flipped_rect,
+ * but possibly translated in the case that flipping is still ineffective in
+ * keeping @window on-screen.
+ *
+ * Since: 3.22
+ * Stability: Private
+ */
+ 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 +6178,60 @@ 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: (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 the window that @window is
+ * 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, which is equivalent to
+ * offsetting the position of @window.
+ *
+ * @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.
+ *
+ * Connect to the #GdkWindow::moved-to-rect signal to find out how it was
+ * actually positioned.
+ *
+ * Since: 3.22
+ * Stability: Private
+ */
+void
+gdk_window_move_to_rect (GdkWindow *window,
+ const GdkRectangle *rect,
+ GdkGravity rect_anchor,
+ GdkGravity window_anchor,
+ GdkAnchorHints anchor_hints,
+ gint rect_anchor_dx,
+ gint rect_anchor_dy)
+{
+ GdkWindowImplClass *impl_class;
+
+ g_return_if_fail (GDK_IS_WINDOW (window));
+ g_return_if_fail (window->transient_for);
+ g_return_if_fail (rect);
+
+ impl_class = GDK_WINDOW_IMPL_GET_CLASS (window->impl);
+ impl_class->move_to_rect (window,
+ 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 4d98e07215..8692499268 100644
--- a/gdk/gdkwindow.h
+++ b/gdk/gdkwindow.h
@@ -245,6 +245,49 @@ 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
+ * Stability: Unstable
+ */
+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 b7ec4eda24..11e49d5bc7 100644
--- a/gdk/gdkwindowimpl.c
+++ b/gdk/gdkwindowimpl.c
@@ -39,6 +39,258 @@ 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,
+ 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 (window->transient_for,
+ root_rect.x,
+ root_rect.y,
+ &root_rect.x,
+ &root_rect.y);
+
+ display = get_display_for_window (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 = 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 +302,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 4ccdc67fea..c72472372d 100644
--- a/gdk/gdkwindowimpl.h
+++ b/gdk/gdkwindowimpl.h
@@ -75,6 +75,13 @@ struct _GdkWindowImplClass
gint y,
gint width,
gint height);
+ void (* move_to_rect) (GdkWindow *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);