diff options
author | William Hua <william.hua@canonical.com> | 2016-06-15 11:00:38 -0400 |
---|---|---|
committer | William Hua <william.hua@canonical.com> | 2016-07-19 09:38:54 -0400 |
commit | b3a530cb727c504923d047347b982a9fcd1490b7 (patch) | |
tree | 32d40b7af0db27bb1a10dff0a6a3282efb39e73f | |
parent | 48108c401ec474a3841b93745e563b4645a3f0d0 (diff) | |
download | gtk+-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.txt | 1 | ||||
-rw-r--r-- | gdk/gdk-private.c | 3 | ||||
-rw-r--r-- | gdk/gdk-private.h | 16 | ||||
-rw-r--r-- | gdk/gdkmarshalers.list | 1 | ||||
-rw-r--r-- | gdk/gdkwindow.c | 95 | ||||
-rw-r--r-- | gdk/gdkwindow.h | 43 | ||||
-rw-r--r-- | gdk/gdkwindowimpl.c | 253 | ||||
-rw-r--r-- | gdk/gdkwindowimpl.h | 7 |
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); |