gdkwindow: add gdk_window_move_to_rect ()

https://bugzilla.gnome.org/show_bug.cgi?id=756579
This commit is contained in:
William Hua 2016-06-15 11:00:38 -04:00
parent 48108c401e
commit b3a530cb72
8 changed files with 418 additions and 1 deletions

View File

@ -341,6 +341,7 @@ GdkWindowWindowClass
GdkWindowHints
GdkGeometry
GdkGravity
GdkAnchorHints
GdkWindowEdge
GdkWindowTypeHint
GdkWindowAttr

View File

@ -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;

View File

@ -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

View File

@ -5,3 +5,4 @@ OBJECT:VOID
OBJECT:DOUBLE,DOUBLE
BOXED:INT,INT
VOID:DOUBLE,DOUBLE,POINTER,POINTER
VOID:POINTER,POINTER,BOOLEAN,BOOLEAN

View File

@ -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:

View File

@ -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:

View File

@ -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;
}

View File

@ -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);