gdk/surface: Replace move_to_rect() with GdkPopupLayout based API

Replace the gdk_surface_move_to_rect() API with a new GdkSurface
method called gdk_surface_present_popup() taking a new GdkPopupLayout
object describing how they should be laid out on screen.

The layout properties provided are the same as the ones used with
gdk_surface_move_to_rect(), except they are now set up using
GdkPopupLayout.

Calling gdk_surface_present_popup() will either show the popup at the
position described using the popup layout object and a new unconstrained
size, or reposition it accordingly.

In some situations, such as when a popup is set to autohide, presenting
may immediately fail, in case the grab was not granted by the display
server.

After a successful present, the result of the layout can be queried
using the following methods:

 * gdk_surface_get_position() - to get the position relative to its
   parent
 * gdk_surface_get_width() - to get the current width
 * gdk_surface_get_height() - to get the current height
 * gdk_surface_get_rect_anchor() - to get the anchor point on the anchor
   rectangle the popup was effectively positioned against given
   constraints defined by the environment and the layout rules provided
   via GdkPopupLayout.
 * gdk_surface_get_surface_anchor() - the same as the one above but for
   the surface anchor.

A new signal replaces the old "moved-to-rect" one -
"popup-layout-changed". However, it is only intended to be emitted when
the layout changes implicitly by the windowing system, for example if
the monitor resolution changed, or the parent window moved.
This commit is contained in:
Jonas Ådahl 2020-02-16 12:59:24 +01:00
parent 37f4c644d3
commit ca71119a40
16 changed files with 1638 additions and 714 deletions

View File

@ -205,7 +205,9 @@ gdk_surface_set_keep_above
gdk_surface_set_keep_below
gdk_surface_set_opacity
gdk_surface_resize
gdk_surface_move_to_rect
gdk_surface_present_popup
gdk_surface_get_popup_rect_anchor
gdk_surface_get_popup_surface_anchor
gdk_surface_raise
gdk_surface_lower
gdk_surface_restack

View File

@ -455,18 +455,21 @@ gdk_broadway_surface_move (GdkSurface *surface,
}
static void
gdk_broadway_surface_moved_to_rect (GdkSurface *surface,
GdkRectangle final_rect)
gdk_broadway_surface_layout_popup (GdkSurface *surface,
int width,
int height,
GdkPopupLayout *layout)
{
GdkSurface *toplevel;
GdkRectangle final_rect;
int x, y;
if (surface->surface_type == GDK_SURFACE_POPUP)
toplevel = surface->parent;
else
toplevel = surface->transient_for;
gdk_surface_layout_popup_helper (surface,
width,
height,
layout,
&final_rect);
gdk_surface_get_origin (toplevel, &x, &y);
gdk_surface_get_origin (surface->parent, &x, &y);
x += final_rect.x;
y += final_rect.y;
@ -474,8 +477,10 @@ gdk_broadway_surface_moved_to_rect (GdkSurface *surface,
final_rect.height != surface->height)
{
gdk_broadway_surface_move_resize (surface,
x, y,
final_rect.width, final_rect.height);
x,
y,
final_rect.width,
final_rect.height);
}
else
{
@ -484,22 +489,49 @@ gdk_broadway_surface_moved_to_rect (GdkSurface *surface,
}
static void
gdk_broadway_surface_move_to_rect (GdkSurface *surface,
const GdkRectangle *rect,
GdkGravity rect_anchor,
GdkGravity surface_anchor,
GdkAnchorHints anchor_hints,
gint rect_anchor_dx,
gint rect_anchor_dy)
show_popup (GdkSurface *surface)
{
gdk_surface_move_to_rect_helper (surface,
rect,
rect_anchor,
surface_anchor,
anchor_hints,
rect_anchor_dx,
rect_anchor_dy,
gdk_broadway_surface_moved_to_rect);
gdk_surface_raise (surface);
gdk_synthesize_surface_state (surface, GDK_SURFACE_STATE_WITHDRAWN, 0);
_gdk_surface_update_viewable (surface);
gdk_broadway_surface_show (surface, FALSE);
gdk_surface_invalidate_rect (surface, NULL);
}
static void
show_grabbing_popup (GdkSeat *seat,
GdkSurface *surface,
gpointer user_data)
{
show_popup (surface);
}
static gboolean
gdk_broadway_surface_present_popup (GdkSurface *surface,
int width,
int height,
GdkPopupLayout *layout)
{
gdk_broadway_surface_layout_popup (surface, width, height, layout);
if (GDK_SURFACE_IS_MAPPED (surface))
return TRUE;
if (surface->autohide)
{
gdk_seat_grab (gdk_display_get_default_seat (surface->display),
surface,
GDK_SEAT_CAPABILITY_ALL,
TRUE,
NULL, NULL,
show_grabbing_popup, NULL);
}
else
{
show_popup (surface);
}
return GDK_SURFACE_IS_MAPPED (surface);
}
static void
@ -1393,7 +1425,7 @@ gdk_broadway_surface_class_init (GdkBroadwaySurfaceClass *klass)
impl_class->lower = gdk_broadway_surface_lower;
impl_class->restack_toplevel = gdk_broadway_surface_restack_toplevel;
impl_class->toplevel_resize = gdk_broadway_surface_toplevel_resize;
impl_class->move_to_rect = gdk_broadway_surface_move_to_rect;
impl_class->present_popup = gdk_broadway_surface_present_popup;
impl_class->get_geometry = gdk_broadway_surface_get_geometry;
impl_class->get_root_coords = gdk_broadway_surface_get_root_coords;
impl_class->get_device_state = gdk_broadway_surface_get_device_state;

View File

@ -35,6 +35,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkGLContext, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkKeymap, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkMonitor, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkSeat, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkPopupLayout, gdk_popup_layout_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkVulkanContext, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkSurface, g_object_unref)

281
gdk/gdkpopuplayout.c Normal file
View File

@ -0,0 +1,281 @@
/* GDK - The GIMP Drawing Kit
* Copyright (C) 2020 Red Hat
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "config.h"
#include "gdkpopuplayout.h"
#include "gdksurface.h"
struct _GdkPopupLayout
{
/* < private >*/
grefcount ref_count;
GdkRectangle anchor_rect;
GdkGravity rect_anchor;
GdkGravity surface_anchor;
GdkAnchorHints anchor_hints;
int dx;
int dy;
gboolean is_sealed;
};
G_DEFINE_BOXED_TYPE (GdkPopupLayout, gdk_popup_layout,
gdk_popup_layout_ref,
gdk_popup_layout_unref)
/**
* gdk_popup_layout_new: (constructor)
* @anchor_rect: (not nullable): the anchor #GdkRectangle to align @surface with
* @rect_anchor: the point on @anchor_rect to align with @surface's anchor point
* @surface_anchor: the point on @surface to align with @rect's anchor point
*
* Create a popup layout description. Used together with
* gdk_surface_present_popup() to describe how a popup surface should be placed
* and behave on-screen.
*
* @anchor_rect is relative to the top-left corner of the surface's parent.
* @rect_anchor and @surface_anchor determine anchor points on @anchor_rect and
* surface to pin together.
*
* The position of @anchor_rect's anchor point can optionally be offset using
* gdk_popup_layout_set_offset(), which is equivalent to offsetting the
* position of surface.
*
* Returns: (transfer full): newly created instance of #GdkPopupLayout
*/
GdkPopupLayout *
gdk_popup_layout_new (const GdkRectangle *anchor_rect,
GdkGravity rect_anchor,
GdkGravity surface_anchor)
{
GdkPopupLayout *layout;
layout = g_new0 (GdkPopupLayout, 1);
g_ref_count_init (&layout->ref_count);
layout->anchor_rect = *anchor_rect;
layout->rect_anchor = rect_anchor;
layout->surface_anchor = surface_anchor;
return layout;
}
/**
* gdk_popup_layout_ref:
* @layout: a #GdkPopupLayout
*
* Increases the reference count of @value.
*
* Returns: the same @layout
*/
GdkPopupLayout *
gdk_popup_layout_ref (GdkPopupLayout *layout)
{
g_ref_count_inc (&layout->ref_count);
return layout;
}
/**
* gdk_popup_layout_unref:
* @layout: a #GdkPopupLayout
*
* Decreases the reference count of @value.
*/
void
gdk_popup_layout_unref (GdkPopupLayout *layout)
{
if (g_ref_count_dec (&layout->ref_count))
g_free (layout);
}
/**
* gdk_popup_layout_copy:
* @layout: a #GdkPopupLayout
*
* Create a new #GdkPopupLayout and copy the contents of @layout into it.
*
* Returns: (transfer full): a copy of @layout.
*/
GdkPopupLayout *
gdk_popup_layout_copy (GdkPopupLayout *layout)
{
GdkPopupLayout *new_layout;
new_layout = g_new0 (GdkPopupLayout, 1);
g_ref_count_init (&new_layout->ref_count);
new_layout->anchor_rect = layout->anchor_rect;
new_layout->rect_anchor = layout->rect_anchor;
new_layout->surface_anchor = layout->surface_anchor;
new_layout->anchor_hints = layout->anchor_hints;
new_layout->dx = layout->dx;
new_layout->dy = layout->dy;
return new_layout;
}
/**
* gdk_popup_layout_set_anchor_rect:
* @layout: a #GdkPopupLayout
* @anchor_rect: the new anchor rectangle
*
* Set the anchor rectangle.
*/
void
gdk_popup_layout_set_anchor_rect (GdkPopupLayout *layout,
const GdkRectangle *anchor_rect)
{
layout->anchor_rect = *anchor_rect;
}
/**
* gdk_popup_layout_get_anchor_rect:
* @layout: a #GdkPopupLayout
*
* Get the anchor rectangle.
*
* Returns: The anchor rectangle.
*/
const GdkRectangle *
gdk_popup_layout_get_anchor_rect (GdkPopupLayout *layout)
{
return &layout->anchor_rect;
}
/**
* gdk_popup_layout_set_rect_anchor:
* @layout: a #GdkPopupLayout
* @anchor: the new rect anchor
*
* Set the anchor on the anchor rectangle.
*/
void
gdk_popup_layout_set_rect_anchor (GdkPopupLayout *layout,
GdkGravity anchor)
{
layout->rect_anchor = anchor;
}
/**
* gdk_popup_layout_get_rect_anchor:
* @layout: a #GdkPopupLayout
*
* Returns: the anchor on the anchor rectangle.
*/
GdkGravity
gdk_popup_layout_get_rect_anchor (GdkPopupLayout *layout)
{
return layout->rect_anchor;
}
/**
* gdk_popup_layout_set_surface_anchor:
* @layout: a #GdkPopupLayout
* @anchor: the new popup surface anchor
*
* Set the anchor on the popup surface.
*/
void
gdk_popup_layout_set_surface_anchor (GdkPopupLayout *layout,
GdkGravity anchor)
{
layout->surface_anchor = anchor;
}
/**
* gdk_popup_layout_get_surface_anchor:
* @layout: a #GdkPopupLayout
*
* Returns: the anchor on the popup surface.
*/
GdkGravity
gdk_popup_layout_get_surface_anchor (GdkPopupLayout *layout)
{
return layout->surface_anchor;
}
/**
* gdk_popup_layout_set_anchor_hints:
* @layout: a #GdkPopupLayout
* @anchor_hints: the new #GdkAnchorHints
*
* Set new anchor hints.
*
* The set @anchor_hints determines how @surface 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 @surface extends beyond the left or right edges of the monitor.
*/
void
gdk_popup_layout_set_anchor_hints (GdkPopupLayout *layout,
GdkAnchorHints anchor_hints)
{
layout->anchor_hints = anchor_hints;
}
/**
* gdk_popup_layout_get_anchor_hints:
* @layout: a #GdkPopupLayout
*
* Get the #GdkAnchorHints.
*
* Returns: the #GdkAnchorHints.
*/
GdkAnchorHints
gdk_popup_layout_get_anchor_hints (GdkPopupLayout *layout)
{
return layout->anchor_hints;
}
/**
* gdk_popup_layout_set_offset:
* @layout: a #GdkPopupLayout
* @dx: x delta to offset the anchor rectangle with
* @dy: y delta to offset the anchor rectangle with
*
* Offset the position of the anchor rectangle with the given delta.
*/
void
gdk_popup_layout_set_offset (GdkPopupLayout *layout,
int dx,
int dy)
{
layout->dx = dx;
layout->dy = dy;
}
/**
* gdk_popup_layout_get_offset:
* @layout: a #GdkPopupLayout
* @dx: a pointer to where to store the delta x coordinate
* @dy: a pointer to where to store the delta y coordinate
*
* Get the delta the anchor rectangle is offset with
*/
void
gdk_popup_layout_get_offset (GdkPopupLayout *layout,
int *dx,
int *dy)
{
if (dx)
*dx = layout->dx;
if (dy)
*dy = layout->dy;
}

136
gdk/gdkpopuplayout.h Normal file
View File

@ -0,0 +1,136 @@
/* GDK - The GIMP Drawing Kit
* Copyright (C) 2020 Red Hat
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef __GDK_POPUP_LAYOUT_H__
#define __GDK_POPUP_LAYOUT_H__
#if !defined (__GDK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gdk/gdk.h> can be included directly."
#endif
#include <gdk/gdkversionmacros.h>
#include <gdk/gdktypes.h>
G_BEGIN_DECLS
/**
* GdkAnchorHints:
* @GDK_ANCHOR_FLIP_X: allow flipping anchors horizontally
* @GDK_ANCHOR_FLIP_Y: allow flipping anchors vertically
* @GDK_ANCHOR_SLIDE_X: allow sliding surface horizontally
* @GDK_ANCHOR_SLIDE_Y: allow sliding surface vertically
* @GDK_ANCHOR_RESIZE_X: allow resizing surface horizontally
* @GDK_ANCHOR_RESIZE_Y: allow resizing surface vertically
* @GDK_ANCHOR_FLIP: allow flipping anchors on both axes
* @GDK_ANCHOR_SLIDE: allow sliding surface on both axes
* @GDK_ANCHOR_RESIZE: allow resizing surface on both axes
*
* Positioning hints for aligning a surface relative to a rectangle.
*
* These hints determine how the surface should be positioned in the case that
* the surface 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 surface extends beyond the left
* or right edges of the monitor.
*
* If %GDK_ANCHOR_SLIDE_X is set, the surface can be shifted horizontally to fit
* on-screen. If %GDK_ANCHOR_RESIZE_X is set, the surface 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.
*/
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;
/**
* GdkPopupLayout:
*/
typedef struct _GdkPopupLayout GdkPopupLayout;
#define GDK_TYPE_POPUP_LAYOUT (gdk_popup_layout_get_type ())
GDK_AVAILABLE_IN_ALL
GType gdk_popup_layout_get_type (void);
GDK_AVAILABLE_IN_ALL
GdkPopupLayout * gdk_popup_layout_new (const GdkRectangle *anchor_rect,
GdkGravity rect_anchor,
GdkGravity surface_anchor);
GDK_AVAILABLE_IN_ALL
GdkPopupLayout * gdk_popup_layout_ref (GdkPopupLayout *layout);
GDK_AVAILABLE_IN_ALL
void gdk_popup_layout_unref (GdkPopupLayout *layout);
GDK_AVAILABLE_IN_ALL
GdkPopupLayout * gdk_popup_layout_copy (GdkPopupLayout *layout);
GDK_AVAILABLE_IN_ALL
void gdk_popup_layout_set_anchor_rect (GdkPopupLayout *layout,
const GdkRectangle *anchor_rect);
GDK_AVAILABLE_IN_ALL
const GdkRectangle * gdk_popup_layout_get_anchor_rect (GdkPopupLayout *layout);
GDK_AVAILABLE_IN_ALL
void gdk_popup_layout_set_rect_anchor (GdkPopupLayout *layout,
GdkGravity anchor);
GDK_AVAILABLE_IN_ALL
GdkGravity gdk_popup_layout_get_rect_anchor (GdkPopupLayout *layout);
GDK_AVAILABLE_IN_ALL
void gdk_popup_layout_set_surface_anchor (GdkPopupLayout *layout,
GdkGravity anchor);
GDK_AVAILABLE_IN_ALL
GdkGravity gdk_popup_layout_get_surface_anchor (GdkPopupLayout *layout);
GDK_AVAILABLE_IN_ALL
void gdk_popup_layout_set_anchor_hints (GdkPopupLayout *layout,
GdkAnchorHints anchor_hints);
GDK_AVAILABLE_IN_ALL
GdkAnchorHints gdk_popup_layout_get_anchor_hints (GdkPopupLayout *layout);
GDK_AVAILABLE_IN_ALL
void gdk_popup_layout_set_offset (GdkPopupLayout *layout,
int dx,
int dy);
GDK_AVAILABLE_IN_ALL
void gdk_popup_layout_get_offset (GdkPopupLayout *layout,
int *dx,
int *dy);
G_END_DECLS
#endif /* __GDK_POPUP_LAYOUT_H__ */

View File

@ -69,7 +69,7 @@
*/
enum {
MOVED_TO_RECT,
POPUP_LAYOUT_CHANGED,
SIZE_CHANGED,
RENDER,
EVENT,
@ -247,36 +247,30 @@ maybe_flip_position (gint bounds_pos,
}
void
gdk_surface_move_to_rect_helper (GdkSurface *surface,
const GdkRectangle *rect,
GdkGravity rect_anchor,
GdkGravity surface_anchor,
GdkAnchorHints anchor_hints,
gint rect_anchor_dx,
gint rect_anchor_dy,
GdkSurfaceMovedToRect moved_to_rect)
gdk_surface_layout_popup_helper (GdkSurface *surface,
int width,
int height,
GdkPopupLayout *layout,
GdkRectangle *out_final_rect)
{
GdkSurface *toplevel;
GdkDisplay *display;
GdkMonitor *monitor;
GdkRectangle bounds;
GdkRectangle root_rect = *rect;
GdkRectangle flipped_rect;
GdkRectangle root_rect;
GdkGravity rect_anchor;
GdkGravity surface_anchor;
int rect_anchor_dx;
int rect_anchor_dy;
GdkAnchorHints anchor_hints;
GdkRectangle final_rect;
gboolean flipped_x;
gboolean flipped_y;
int x, y;
/* This implementation only works for backends that
* can provide root coordinates via get_root_coords.
* Other backends need to implement move_to_rect.
*/
if (surface->surface_type == GDK_SURFACE_POPUP)
toplevel = surface->parent;
else
toplevel = surface->transient_for;
g_return_if_fail (surface->surface_type == GDK_SURFACE_POPUP);
gdk_surface_get_root_coords (toplevel,
root_rect = *gdk_popup_layout_get_anchor_rect (layout);
gdk_surface_get_root_coords (surface->parent,
root_rect.x,
root_rect.y,
&root_rect.x,
@ -286,30 +280,33 @@ gdk_surface_move_to_rect_helper (GdkSurface *surface,
monitor = get_monitor_for_rect (display, &root_rect);
gdk_monitor_get_workarea (monitor, &bounds);
flipped_rect.width = surface->width - surface->shadow_left - surface->shadow_right;
flipped_rect.height = surface->height - surface->shadow_top - surface->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 (surface_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 (surface_anchor),
rect_anchor_dy,
anchor_hints & GDK_ANCHOR_FLIP_Y,
&flipped_y);
rect_anchor = gdk_popup_layout_get_rect_anchor (layout);
surface_anchor = gdk_popup_layout_get_surface_anchor (layout);
gdk_popup_layout_get_offset (layout, &rect_anchor_dx, &rect_anchor_dy);
anchor_hints = gdk_popup_layout_get_anchor_hints (layout);
final_rect = flipped_rect;
final_rect.width = width - surface->shadow_left - surface->shadow_right;
final_rect.height = height - surface->shadow_top - surface->shadow_bottom;
final_rect.x = maybe_flip_position (bounds.x,
bounds.width,
root_rect.x,
root_rect.width,
final_rect.width,
get_anchor_x_sign (rect_anchor),
get_anchor_x_sign (surface_anchor),
rect_anchor_dx,
anchor_hints & GDK_ANCHOR_FLIP_X,
&flipped_x);
final_rect.y = maybe_flip_position (bounds.y,
bounds.height,
root_rect.y,
root_rect.height,
final_rect.height,
get_anchor_y_sign (rect_anchor),
get_anchor_y_sign (surface_anchor),
rect_anchor_dy,
anchor_hints & GDK_ANCHOR_FLIP_Y,
&flipped_y);
if (anchor_hints & GDK_ANCHOR_SLIDE_X)
{
@ -353,30 +350,30 @@ gdk_surface_move_to_rect_helper (GdkSurface *surface,
final_rect.height = bounds.y + bounds.height - final_rect.y;
}
flipped_rect.x -= surface->shadow_left;
flipped_rect.y -= surface->shadow_top;
flipped_rect.width += surface->shadow_left + surface->shadow_right;
flipped_rect.height += surface->shadow_top + surface->shadow_bottom;
final_rect.x -= surface->shadow_left;
final_rect.y -= surface->shadow_top;
final_rect.width += surface->shadow_left + surface->shadow_right;
final_rect.height += surface->shadow_top + surface->shadow_bottom;
gdk_surface_get_origin (toplevel, &x, &y);
gdk_surface_get_origin (surface->parent, &x, &y);
final_rect.x -= x;
final_rect.y -= y;
flipped_rect.x -= x;
flipped_rect.y -= y;
moved_to_rect (surface, final_rect);
if (flipped_x)
{
rect_anchor = gdk_gravity_flip_horizontally (rect_anchor);
surface_anchor = gdk_gravity_flip_horizontally (surface_anchor);
}
if (flipped_y)
{
rect_anchor = gdk_gravity_flip_vertically (rect_anchor);
surface_anchor = gdk_gravity_flip_vertically (surface_anchor);
}
g_signal_emit_by_name (surface,
"moved-to-rect",
&flipped_rect,
&final_rect,
flipped_x,
flipped_y);
surface->popup.rect_anchor = rect_anchor;
surface->popup.surface_anchor = surface_anchor;
*out_final_rect = final_rect;
}
static void
@ -481,42 +478,24 @@ gdk_surface_class_init (GdkSurfaceClass *klass)
g_object_class_install_properties (object_class, LAST_PROP, properties);
/**
* GdkSurface::moved-to-rect:
* @surface: the #GdkSurface that moved
* @flipped_rect: (nullable): the position of @surface after any possible
* flipping or %NULL if the backend can't obtain it
* @final_rect: (nullable): the final position of @surface 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
* GdkSurface::popup-layout-changed
* @surface: the #GdkSurface that was laid out
*
* Emitted when the position of @surface is finalized after being moved to a
* destination rectangle.
* Emitted when the layout of a popup @surface has changed, e.g. if the popup
* layout was reactive and after the parent moved causing the popover to end
* up partially off-screen.
*
* @surface 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 @surface 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 @surface on-screen.
* Stability: Private
*/
signals[MOVED_TO_RECT] =
g_signal_new (g_intern_static_string ("moved-to-rect"),
signals[POPUP_LAYOUT_CHANGED] =
g_signal_new (g_intern_static_string ("popup-layout-changed"),
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
0,
NULL,
NULL,
_gdk_marshal_VOID__POINTER_POINTER_BOOLEAN_BOOLEAN,
NULL,
G_TYPE_NONE,
4,
G_TYPE_POINTER,
G_TYPE_POINTER,
G_TYPE_BOOLEAN,
G_TYPE_BOOLEAN);
0);
/**
* GdkSurface::size-changed:
@ -805,9 +784,8 @@ gdk_surface_new_temp (GdkDisplay *display,
*
* Create a new popup surface.
*
* The surface will be attached to @parent and can
* be positioned relative to it using
* gdk_surface_move_to_rect().
* The surface will be attached to @parent and can be positioned relative to it
* using gdk_surface_show_popup() or later using gdk_surface_layout_popup().
*
* Returns: (transfer full): a new #GdkSurface
*/
@ -1985,14 +1963,6 @@ gdk_surface_restack (GdkSurface *surface,
GDK_SURFACE_GET_CLASS (surface)->restack_toplevel (surface, sibling, above);
}
static void
grab_prepare_func (GdkSeat *seat,
GdkSurface *surface,
gpointer data)
{
gdk_surface_show_internal (surface, TRUE);
}
/**
* gdk_surface_show:
* @surface: a #GdkSurface
@ -2004,25 +1974,18 @@ grab_prepare_func (GdkSeat *seat,
* This function maps a surface so its visible onscreen. Its opposite
* is gdk_surface_hide().
*
* This function may not be used on a #GdkSurface with the surface type
* GTK_SURFACE_POPUP.
*
* When implementing a #GtkWidget, you should call this function on the widget's
* #GdkSurface as part of the map method.
*/
void
gdk_surface_show (GdkSurface *surface)
{
if (surface->autohide)
{
gdk_seat_grab (gdk_display_get_default_seat (surface->display),
surface,
GDK_SEAT_CAPABILITY_ALL,
TRUE,
NULL, NULL,
grab_prepare_func, NULL);
}
else
{
gdk_surface_show_internal (surface, TRUE);
}
g_return_if_fail (surface->surface_type != GDK_SURFACE_POPUP);
gdk_surface_show_internal (surface, TRUE);
}
/**
@ -2084,6 +2047,8 @@ G_GNUC_END_IGNORE_DEPRECATIONS
GDK_SURFACE_GET_CLASS (surface)->hide (surface);
surface->popup.rect_anchor = 0;
surface->popup.surface_anchor = 0;
surface->x = 0;
surface->y = 0;
}
@ -2109,52 +2074,70 @@ gdk_surface_resize (GdkSurface *surface,
}
/**
* gdk_surface_move_to_rect:
* @surface: the #GdkSurface to move
* @rect: (not nullable): the destination #GdkRectangle to align @surface with
* @rect_anchor: the point on @rect to align with @surface's anchor point
* @surface_anchor: the point on @surface to align with @rect's anchor point
* @anchor_hints: positioning hints to use when limited on space
* @rect_anchor_dx: horizontal offset to shift @surface, i.e. @rect's anchor
* point
* @rect_anchor_dy: vertical offset to shift @surface, i.e. @rect's anchor point
* gdk_surface_present_popup:
* @surface: the popup #GdkSurface to show
* @width: the unconstrained popup width to layout
* @height: the unconstrained popup height to layout
* @layout: the #GdkPopupLayout object used to layout
*
* Moves @surface to @rect, aligning their anchor points.
* Present @surface after having processed the #GdkPopupLayout rules. If the
* popup was previously now showing, it will be showed, otherwise it will
* change position according to @layout.
*
* @rect is relative to the top-left corner of the surface that @surface is
* transient for. @rect_anchor and @surface_anchor determine anchor points on
* @rect and @surface 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 @surface.
* After calling this function, the result of the layout can be queried
* using gdk_surface_get_position(), gdk_surface_get_width(),
* gdk_surface_get_height(), gdk_surface_get_popup_rect_anchor() and
* gdk_surface_get_popup_surface_anchor().
*
* @anchor_hints determines how @surface 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
* @surface extends beyond the left or right edges of the monitor.
* Presenting may have fail, for example if it was immediately hidden if the
* @surface was set to autohide.
*
* Connect to the #GdkSurface::moved-to-rect signal to find out how it was
* actually positioned.
* Returns: %FALSE if it failed to be presented, otherwise %TRUE.
*/
void
gdk_surface_move_to_rect (GdkSurface *surface,
const GdkRectangle *rect,
GdkGravity rect_anchor,
GdkGravity surface_anchor,
GdkAnchorHints anchor_hints,
gint rect_anchor_dx,
gint rect_anchor_dy)
gboolean
gdk_surface_present_popup (GdkSurface *surface,
int width,
int height,
GdkPopupLayout *layout)
{
g_return_if_fail (GDK_IS_SURFACE (surface));
g_return_if_fail (surface->parent || surface->transient_for);
g_return_if_fail (rect);
g_return_val_if_fail (GDK_IS_SURFACE (surface), FALSE);
g_return_val_if_fail (surface->parent, FALSE);
g_return_val_if_fail (layout, FALSE);
g_return_val_if_fail (!GDK_SURFACE_DESTROYED (surface), FALSE);
g_return_val_if_fail (width > 0 && height > 0, FALSE);
GDK_SURFACE_GET_CLASS (surface)->move_to_rect (surface,
rect,
rect_anchor,
surface_anchor,
anchor_hints,
rect_anchor_dx,
rect_anchor_dy);
return GDK_SURFACE_GET_CLASS (surface)->present_popup (surface,
width,
height,
layout);
}
/**
* gdk_surface_get_popup_surface_anchor:
* @surface: a #GdkSurface
*
* Get the current popup surface anchor. The value returned may chage after
* calling gdk_surface_show_popup(), gdk_surface_layout_popup() or after the
* "popup-layout-changed" is emitted.
*/
GdkGravity
gdk_surface_get_popup_surface_anchor (GdkSurface *surface)
{
return surface->popup.surface_anchor;
}
/**
* gdk_surface_get_popup_rect_anchor:
* @surface: a #GdkSurface
*
* Get the current popup anchor rectangle anchor. The value
* returned may chage after calling gdk_surface_show_popup(),
* gdk_surface_layout_popup() or after the "popup-layout-changed" is emitted.
*/
GdkGravity
gdk_surface_get_popup_rect_anchor (GdkSurface *surface)
{
return surface->popup.rect_anchor;
}
static void

View File

@ -34,6 +34,7 @@
#include <gdk/gdkevents.h>
#include <gdk/gdkframeclock.h>
#include <gdk/gdkmonitor.h>
#include <gdk/gdkpopuplayout.h>
G_BEGIN_DECLS
@ -143,47 +144,6 @@ typedef enum
GDK_FUNC_CLOSE = 1 << 5
} GdkWMFunction;
/**
* GdkAnchorHints:
* @GDK_ANCHOR_FLIP_X: allow flipping anchors horizontally
* @GDK_ANCHOR_FLIP_Y: allow flipping anchors vertically
* @GDK_ANCHOR_SLIDE_X: allow sliding surface horizontally
* @GDK_ANCHOR_SLIDE_Y: allow sliding surface vertically
* @GDK_ANCHOR_RESIZE_X: allow resizing surface horizontally
* @GDK_ANCHOR_RESIZE_Y: allow resizing surface vertically
* @GDK_ANCHOR_FLIP: allow flipping anchors on both axes
* @GDK_ANCHOR_SLIDE: allow sliding surface on both axes
* @GDK_ANCHOR_RESIZE: allow resizing surface on both axes
*
* Positioning hints for aligning a surface relative to a rectangle.
*
* These hints determine how the surface should be positioned in the case that
* the surface 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 surface extends beyond the left
* or right edges of the monitor.
*
* If %GDK_ANCHOR_SLIDE_X is set, the surface can be shifted horizontally to fit
* on-screen. If %GDK_ANCHOR_RESIZE_X is set, the surface 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.
*/
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;
/**
* GdkSurfaceEdge:
* @GDK_SURFACE_EDGE_NORTH_WEST: the top left corner.
@ -407,13 +367,14 @@ void gdk_surface_resize (GdkSurface *surface,
gint width,
gint height);
GDK_AVAILABLE_IN_ALL
void gdk_surface_move_to_rect (GdkSurface *surface,
const GdkRectangle *rect,
GdkGravity rect_anchor,
GdkGravity surface_anchor,
GdkAnchorHints anchor_hints,
gint rect_anchor_dx,
gint rect_anchor_dy);
gboolean gdk_surface_present_popup (GdkSurface *surface,
int width,
int height,
GdkPopupLayout *layout);
GDK_AVAILABLE_IN_ALL
GdkGravity gdk_surface_get_popup_surface_anchor (GdkSurface *surface);
GDK_AVAILABLE_IN_ALL
GdkGravity gdk_surface_get_popup_rect_anchor (GdkSurface *surface);
GDK_AVAILABLE_IN_ALL
void gdk_surface_raise (GdkSurface *surface);

View File

@ -73,6 +73,11 @@ struct _GdkSurface
guint frame_clock_events_paused : 1;
guint autohide : 1;
struct {
GdkGravity surface_anchor;
GdkGravity rect_anchor;
} popup;
guint update_and_descendants_freeze_count;
gint width, height;
@ -116,13 +121,10 @@ struct _GdkSurfaceClass
void (* toplevel_resize) (GdkSurface *surface,
gint width,
gint height);
void (* move_to_rect) (GdkSurface *surface,
const GdkRectangle *rect,
GdkGravity rect_anchor,
GdkGravity surface_anchor,
GdkAnchorHints anchor_hints,
gint rect_anchor_dx,
gint rect_anchor_dy);
gboolean (* present_popup) (GdkSurface *surface,
int width,
int height,
GdkPopupLayout *layout);
void (* get_geometry) (GdkSurface *surface,
gint *x,
@ -255,18 +257,71 @@ struct _GdkSurfaceClass
void gdk_surface_set_state (GdkSurface *surface,
GdkSurfaceState new_state);
typedef void (* GdkSurfaceMovedToRect) (GdkSurface *surface,
GdkRectangle final_rect);
void gdk_surface_layout_popup_helper (GdkSurface *surface,
int width,
int height,
GdkPopupLayout *layout,
GdkRectangle *out_final_rect);
void
gdk_surface_move_to_rect_helper (GdkSurface *surface,
const GdkRectangle *rect,
GdkGravity rect_anchor,
GdkGravity surface_anchor,
GdkAnchorHints anchor_hints,
gint rect_anchor_dx,
gint rect_anchor_dy,
GdkSurfaceMovedToRect moved_to_rect);
static inline GdkGravity
gdk_gravity_flip_horizontally (GdkGravity anchor)
{
switch (anchor)
{
default:
case GDK_GRAVITY_STATIC:
case GDK_GRAVITY_NORTH_WEST:
return GDK_GRAVITY_NORTH_EAST;
case GDK_GRAVITY_NORTH:
return GDK_GRAVITY_NORTH;
case GDK_GRAVITY_NORTH_EAST:
return GDK_GRAVITY_NORTH_WEST;
case GDK_GRAVITY_WEST:
return GDK_GRAVITY_EAST;
case GDK_GRAVITY_CENTER:
return GDK_GRAVITY_CENTER;
case GDK_GRAVITY_EAST:
return GDK_GRAVITY_WEST;
case GDK_GRAVITY_SOUTH_WEST:
return GDK_GRAVITY_SOUTH_EAST;
case GDK_GRAVITY_SOUTH:
return GDK_GRAVITY_SOUTH;
case GDK_GRAVITY_SOUTH_EAST:
return GDK_GRAVITY_SOUTH_WEST;
}
g_assert_not_reached ();
}
static inline GdkGravity
gdk_gravity_flip_vertically (GdkGravity anchor)
{
switch (anchor)
{
default:
case GDK_GRAVITY_STATIC:
case GDK_GRAVITY_NORTH_WEST:
return GDK_GRAVITY_SOUTH_WEST;
case GDK_GRAVITY_NORTH:
return GDK_GRAVITY_SOUTH;
case GDK_GRAVITY_NORTH_EAST:
return GDK_GRAVITY_SOUTH_EAST;
case GDK_GRAVITY_WEST:
return GDK_GRAVITY_WEST;
case GDK_GRAVITY_CENTER:
return GDK_GRAVITY_CENTER;
case GDK_GRAVITY_EAST:
return GDK_GRAVITY_EAST;
case GDK_GRAVITY_SOUTH_WEST:
return GDK_GRAVITY_NORTH_WEST;
case GDK_GRAVITY_SOUTH:
return GDK_GRAVITY_NORTH;
case GDK_GRAVITY_SOUTH_EAST:
return GDK_GRAVITY_NORTH_EAST;
}
g_assert_not_reached ();
}
G_END_DECLS

View File

@ -45,6 +45,7 @@ gdk_public_sources = files([
'gdktexture.c',
'gdkvulkancontext.c',
'gdksurface.c',
'gdkpopuplayout.c',
'gdkprofiler.c'
])
@ -90,6 +91,7 @@ gdk_public_headers = files([
'gdktypes.h',
'gdkvulkancontext.h',
'gdksurface.h',
'gdkpopuplayout.h',
])
install_headers(gdk_public_headers, subdir: 'gtk-4.0/gdk/')

View File

@ -1245,51 +1245,83 @@ gdk_surface_quartz_toplevel_resize (GdkSurface *surface,
}
static void
gdk_quartz_surface_moved_to_rect (GdkSurface *surface,
GdkRectangle final_rect)
gdk_quartz_surface_layout_popup (GdkSurface *surface,
int width,
int height,
GdkPopupLayout *layout)
{
GdkSurface *toplevel;
GdkRectangle final_rect;
int x, y;
if (surface->surface_type == GDK_SURFACE_POPUP)
toplevel = surface->parent;
else
toplevel = surface->transient_for;
gdk_surface_layout_popup_helper (surface,
width,
height,
layout,
&final_rect);
gdk_surface_get_origin (toplevel, &x, &y);
gdk_surface_get_origin (surface->parent, &x, &y);
x += final_rect.x;
y += final_rect.y;
if (final_rect.width != surface->width ||
final_rect.height != surface->height)
{
window_quartz_move_resize (surface,
x, y,
final_rect.width, final_rect.height);
move_resize_window_internal (surface,
x,
y,
final_rect.width,
final_rect.height);
}
else
{
window_quartz_resize (surface, final_rect.width, final_rect.height);
window_quartz_move (surface, x, y);
}
}
static void
gdk_quartz_surface_move_to_rect (GdkSurface *surface,
const GdkRectangle *rect,
GdkGravity rect_anchor,
GdkGravity surface_anchor,
GdkAnchorHints anchor_hints,
gint rect_anchor_dx,
gint rect_anchor_dy)
show_popup (GdkSurface *surface)
{
gdk_surface_move_to_rect_helper (surface,
rect,
rect_anchor,
surface_anchor,
anchor_hints,
rect_anchor_dx,
rect_anchor_dy,
gdk_quartz_surface_moved_to_rect);
gdk_surface_raise (surface);
gdk_synthesize_surface_state (surface, GDK_SURFACE_STATE_WITHDRAWN, 0);
_gdk_surface_update_viewable (surface);
gdk_quartz_surface_show (surface, FALSE);
gdk_surface_invalidate_rect (surface, NULL);
}
static void
show_grabbing_popup (GdkSeat *seat,
GdkSurface *surface,
gpointer user_data)
{
show_popup (surface);
}
static gboolean
gdk_quartz_surface_present_popup (GdkSurface *surface,
int width,
int height,
GdkPopupLayout *layout)
{
gdk_quartz_surface_layout_popup (surface, width, height, layout);
if (GDK_SURFACE_IS_MAPPED (surface))
return TRUE;
if (surface->autohide)
{
gdk_seat_grab (gdk_display_get_default_seat (surface->display),
surface,
GDK_SEAT_CAPABILITY_ALL,
TRUE,
NULL, NULL,
show_grabbing_popup, NULL);
}
else
{
show_popup (surface);
}
return GDK_SURFACE_IS_MAPPED (surface);
}
/* Get the toplevel ordering from NSApp and update our own list. We do
@ -2675,7 +2707,7 @@ gdk_surface_impl_quartz_class_init (GdkSurfaceImplQuartzClass *klass)
impl_class->lower = gdk_surface_quartz_lower;
impl_class->restack_toplevel = gdk_surface_quartz_restack_toplevel;
impl_class->toplevel_resize = gdk_surface_quartz_toplevel_resize;
impl_class->move_to_rect = gdk_surface_quartz_move_to_rect;
impl_class->present_popup = gdk_quartz_surface_present_popup;
impl_class->get_geometry = gdk_surface_quartz_get_geometry;
impl_class->get_root_coords = gdk_surface_quartz_get_root_coords;
impl_class->get_device_state = gdk_surface_quartz_get_device_state;

View File

@ -4593,8 +4593,6 @@ gdk_wayland_seat_grab (GdkSeat *seat,
if (!gdk_surface_is_visible (surface))
{
gdk_wayland_seat_set_grab_surface (wayland_seat, NULL);
g_critical ("Surface %p has not been made visible in GdkSeatGrabPrepareFunc",
surface);
return GDK_GRAB_NOT_VIEWABLE;
}

File diff suppressed because it is too large Load Diff

View File

@ -1289,18 +1289,21 @@ gdk_win32_surface_move (GdkSurface *surface,
}
static void
gdk_win32_surface_moved_to_rect (GdkSurface *surface,
GdkRectangle final_rect)
gdk_win32_surface_layout_popup (GdkSurface *surface,
int width,
int height,
GdkPopupLayout *layout)
{
GdkSurface *toplevel;
GdkRectangle final_rect;
int x, y;
if (surface->surface_type == GDK_SURFACE_POPUP)
toplevel = surface->parent;
else
toplevel = surface->transient_for;
gdk_surface_layout_popup_helper (surface,
width,
height,
layout,
&final_rect);
gdk_surface_get_origin (toplevel, &x, &y);
gdk_surface_get_origin (surface->parent, &x, &y);
x += final_rect.x;
y += final_rect.y;
@ -1308,8 +1311,10 @@ gdk_win32_surface_moved_to_rect (GdkSurface *surface,
final_rect.height != surface->height)
{
gdk_win32_surface_move_resize (surface,
x, y,
final_rect.width, final_rect.height);
x,
y,
final_rect.width,
final_rect.height);
}
else
{
@ -1318,22 +1323,49 @@ gdk_win32_surface_moved_to_rect (GdkSurface *surface,
}
static void
gdk_win32_surface_move_to_rect (GdkSurface *surface,
const GdkRectangle *rect,
GdkGravity rect_anchor,
GdkGravity surface_anchor,
GdkAnchorHints anchor_hints,
gint rect_anchor_dx,
gint rect_anchor_dy)
show_popup (GdkSurface *surface)
{
gdk_surface_move_to_rect_helper (surface,
rect,
rect_anchor,
surface_anchor,
anchor_hints,
rect_anchor_dx,
rect_anchor_dy,
gdk_win32_surface_moved_to_rect);
gdk_surface_raise (surface);
gdk_synthesize_surface_state (surface, GDK_SURFACE_STATE_WITHDRAWN, 0);
_gdk_surface_update_viewable (surface);
show_window_internal (surface, FALSE, FALSE);
gdk_surface_invalidate_rect (surface, NULL);
}
static void
show_grabbing_popup (GdkSeat *seat,
GdkSurface *surface,
gpointer user_data)
{
show_popup (surface);
}
static gboolean
gdk_win32_surface_present_popup (GdkSurface *surface,
int width,
int height,
GdkPopupLayout *layout)
{
gdk_win32_surface_layout_popup (surface, width, height, layout);
if (GDK_SURFACE_IS_MAPPED (surface))
return TRUE;
if (surface->autohide)
{
gdk_seat_grab (gdk_display_get_default_seat (surface->display),
surface,
GDK_SEAT_CAPABILITY_ALL,
TRUE,
NULL, NULL,
show_grabbing_popup, NULL);
}
else
{
show_popup (surface);
}
return GDK_SURFACE_IS_MAPPED (surface);
}
static void
@ -5161,7 +5193,7 @@ gdk_win32_surface_class_init (GdkWin32SurfaceClass *klass)
impl_class->lower = gdk_win32_surface_lower;
impl_class->restack_toplevel = gdk_win32_surface_restack_toplevel;
impl_class->toplevel_resize = gdk_win32_surface_toplevel_resize;
impl_class->move_to_rect = gdk_win32_surface_move_to_rect;
impl_class->present_popup = gdk_win32_surface_present_popup;
impl_class->get_geometry = gdk_win32_surface_get_geometry;
impl_class->get_device_state = gdk_surface_win32_get_device_state;
impl_class->get_root_coords = gdk_win32_surface_get_root_coords;

View File

@ -1416,18 +1416,21 @@ gdk_x11_surface_move (GdkSurface *surface,
}
static void
gdk_x11_surface_moved_to_rect (GdkSurface *surface,
GdkRectangle final_rect)
gdk_x11_surface_layout_popup (GdkSurface *surface,
int width,
int height,
GdkPopupLayout *layout)
{
GdkSurface *toplevel;
GdkRectangle final_rect;
int x, y;
if (surface->surface_type == GDK_SURFACE_POPUP)
toplevel = surface->parent;
else
toplevel = surface->transient_for;
gdk_surface_layout_popup_helper (surface,
width,
height,
layout,
&final_rect);
gdk_surface_get_origin (toplevel, &x, &y);
gdk_surface_get_origin (surface->parent, &x, &y);
x += final_rect.x;
y += final_rect.y;
@ -1436,8 +1439,10 @@ gdk_x11_surface_moved_to_rect (GdkSurface *surface,
{
gdk_x11_surface_move_resize (surface,
TRUE,
x, y,
final_rect.width, final_rect.height);
x,
y,
final_rect.width,
final_rect.height);
}
else
{
@ -1446,22 +1451,49 @@ gdk_x11_surface_moved_to_rect (GdkSurface *surface,
}
static void
gdk_x11_surface_move_to_rect (GdkSurface *surface,
const GdkRectangle *rect,
GdkGravity rect_anchor,
GdkGravity surface_anchor,
GdkAnchorHints anchor_hints,
gint rect_anchor_dx,
gint rect_anchor_dy)
show_popup (GdkSurface *surface)
{
gdk_surface_move_to_rect_helper (surface,
rect,
rect_anchor,
surface_anchor,
anchor_hints,
rect_anchor_dx,
rect_anchor_dy,
gdk_x11_surface_moved_to_rect);
gdk_surface_raise (surface);
gdk_synthesize_surface_state (surface, GDK_SURFACE_STATE_WITHDRAWN, 0);
_gdk_surface_update_viewable (surface);
gdk_x11_surface_show (surface, FALSE);
gdk_surface_invalidate_rect (surface, NULL);
}
static void
show_grabbing_popup (GdkSeat *seat,
GdkSurface *surface,
gpointer user_data)
{
show_popup (surface);
}
static gboolean
gdk_x11_surface_present_popup (GdkSurface *surface,
int width,
int height,
GdkPopupLayout *layout)
{
gdk_x11_surface_layout_popup (surface, width, height, layout);
if (GDK_SURFACE_IS_MAPPED (surface))
return TRUE;
if (surface->autohide)
{
gdk_seat_grab (gdk_display_get_default_seat (surface->display),
surface,
GDK_SEAT_CAPABILITY_ALL,
TRUE,
NULL, NULL,
show_grabbing_popup, NULL);
}
else
{
show_popup (surface);
}
return GDK_SURFACE_IS_MAPPED (surface);
}
static void gdk_x11_surface_restack_toplevel (GdkSurface *surface,
@ -4659,7 +4691,7 @@ gdk_x11_surface_class_init (GdkX11SurfaceClass *klass)
impl_class->lower = gdk_x11_surface_lower;
impl_class->restack_toplevel = gdk_x11_surface_restack_toplevel;
impl_class->toplevel_resize = gdk_x11_surface_toplevel_resize;
impl_class->move_to_rect = gdk_x11_surface_move_to_rect;
impl_class->present_popup = gdk_x11_surface_present_popup;
impl_class->get_geometry = gdk_x11_surface_get_geometry;
impl_class->get_root_coords = gdk_x11_surface_get_root_coords;
impl_class->get_device_state = gdk_x11_surface_get_device_state;

View File

@ -155,6 +155,7 @@ typedef struct {
GtkCssNode *arrow_node;
GskRenderNode *arrow_render_node;
GdkPopupLayout *layout;
GdkRectangle final_rect;
GtkPositionType final_position;
} GtkPopoverPrivate;
@ -221,14 +222,200 @@ gtk_popover_native_get_surface_transform (GtkNative *native,
_gtk_css_number_value_get (style->size->padding_top, 100);
}
static gboolean
is_gravity_facing_north (GdkGravity gravity)
{
switch (gravity)
{
case GDK_GRAVITY_NORTH_EAST:
case GDK_GRAVITY_NORTH:
case GDK_GRAVITY_NORTH_WEST:
case GDK_GRAVITY_STATIC:
return TRUE;
case GDK_GRAVITY_SOUTH_WEST:
case GDK_GRAVITY_WEST:
case GDK_GRAVITY_SOUTH_EAST:
case GDK_GRAVITY_EAST:
case GDK_GRAVITY_CENTER:
case GDK_GRAVITY_SOUTH:
return FALSE;
default:
g_assert_not_reached ();
}
}
static gboolean
is_gravity_facing_south (GdkGravity gravity)
{
switch (gravity)
{
case GDK_GRAVITY_SOUTH_WEST:
case GDK_GRAVITY_SOUTH_EAST:
case GDK_GRAVITY_SOUTH:
return TRUE;
case GDK_GRAVITY_NORTH_EAST:
case GDK_GRAVITY_NORTH:
case GDK_GRAVITY_NORTH_WEST:
case GDK_GRAVITY_STATIC:
case GDK_GRAVITY_WEST:
case GDK_GRAVITY_EAST:
case GDK_GRAVITY_CENTER:
return FALSE;
default:
g_assert_not_reached ();
}
}
static gboolean
is_gravity_facing_west (GdkGravity gravity)
{
switch (gravity)
{
case GDK_GRAVITY_NORTH_WEST:
case GDK_GRAVITY_STATIC:
case GDK_GRAVITY_SOUTH_WEST:
case GDK_GRAVITY_WEST:
return TRUE;
case GDK_GRAVITY_NORTH_EAST:
case GDK_GRAVITY_SOUTH_EAST:
case GDK_GRAVITY_EAST:
case GDK_GRAVITY_NORTH:
case GDK_GRAVITY_CENTER:
case GDK_GRAVITY_SOUTH:
return FALSE;
default:
g_assert_not_reached ();
}
}
static gboolean
is_gravity_facing_east (GdkGravity gravity)
{
switch (gravity)
{
case GDK_GRAVITY_NORTH_EAST:
case GDK_GRAVITY_SOUTH_EAST:
case GDK_GRAVITY_EAST:
return TRUE;
case GDK_GRAVITY_NORTH_WEST:
case GDK_GRAVITY_STATIC:
case GDK_GRAVITY_SOUTH_WEST:
case GDK_GRAVITY_WEST:
case GDK_GRAVITY_NORTH:
case GDK_GRAVITY_CENTER:
case GDK_GRAVITY_SOUTH:
return FALSE;
default:
g_assert_not_reached ();
}
}
static gboolean
did_flip_horizontally (GdkGravity original_gravity,
GdkGravity final_gravity)
{
g_return_val_if_fail (original_gravity, FALSE);
g_return_val_if_fail (final_gravity, FALSE);
if (is_gravity_facing_east (original_gravity) &&
is_gravity_facing_west (final_gravity))
return TRUE;
if (is_gravity_facing_west (original_gravity) &&
is_gravity_facing_east (final_gravity))
return TRUE;
return FALSE;
}
static gboolean
did_flip_vertically (GdkGravity original_gravity,
GdkGravity final_gravity)
{
g_return_val_if_fail (original_gravity, FALSE);
g_return_val_if_fail (final_gravity, FALSE);
if (is_gravity_facing_north (original_gravity) &&
is_gravity_facing_south (final_gravity))
return TRUE;
if (is_gravity_facing_south (original_gravity) &&
is_gravity_facing_north (final_gravity))
return TRUE;
return FALSE;
}
static void
move_to_rect (GtkPopover *popover)
update_popover_layout (GtkPopover *popover,
GdkPopupLayout *layout)
{
GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover);
GdkRectangle final_rect;
gboolean flipped_x;
gboolean flipped_y;
g_clear_pointer (&priv->layout, gdk_popup_layout_unref);
priv->layout = layout;
final_rect = (GdkRectangle) {
.width = gdk_surface_get_width (priv->surface),
.height = gdk_surface_get_height (priv->surface),
};
gdk_surface_get_position (priv->surface,
&final_rect.x,
&final_rect.y);
flipped_x =
did_flip_horizontally (gdk_popup_layout_get_rect_anchor (layout),
gdk_surface_get_popup_rect_anchor (priv->surface)) &&
did_flip_horizontally (gdk_popup_layout_get_surface_anchor (layout),
gdk_surface_get_popup_surface_anchor (priv->surface));
flipped_y =
did_flip_vertically (gdk_popup_layout_get_rect_anchor (layout),
gdk_surface_get_popup_rect_anchor (priv->surface)) &&
did_flip_vertically (gdk_popup_layout_get_surface_anchor (layout),
gdk_surface_get_popup_surface_anchor (priv->surface));
gtk_widget_allocate (GTK_WIDGET (popover),
gdk_surface_get_width (priv->surface),
gdk_surface_get_height (priv->surface),
-1, NULL);
priv->final_rect = final_rect;
switch (priv->position)
{
case GTK_POS_LEFT:
priv->final_position = flipped_x ? GTK_POS_RIGHT : GTK_POS_LEFT;
break;
case GTK_POS_RIGHT:
priv->final_position = flipped_x ? GTK_POS_LEFT : GTK_POS_RIGHT;
break;
case GTK_POS_TOP:
priv->final_position = flipped_y ? GTK_POS_BOTTOM : GTK_POS_TOP;
break;
case GTK_POS_BOTTOM:
priv->final_position = flipped_y ? GTK_POS_TOP : GTK_POS_BOTTOM;
break;
default:
g_assert_not_reached ();
break;
}
g_clear_pointer (&priv->arrow_render_node, gsk_render_node_unref);
gtk_widget_queue_draw (GTK_WIDGET (popover));
}
static GdkPopupLayout *
create_popup_layout (GtkPopover *popover)
{
GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover);
GdkRectangle rect;
GdkGravity parent_anchor;
GdkGravity surface_anchor;
GdkAnchorHints anchor_hints;
GdkPopupLayout *layout;
gtk_widget_get_surface_allocation (priv->relative_to, &rect);
if (priv->has_pointing_to)
@ -341,46 +528,39 @@ move_to_rect (GtkPopover *popover)
g_assert_not_reached ();
}
gdk_surface_move_to_rect (priv->surface,
&rect,
parent_anchor,
surface_anchor,
anchor_hints,
0, 0);
layout = gdk_popup_layout_new (&rect,
parent_anchor,
surface_anchor);
gdk_popup_layout_set_anchor_hints (layout, anchor_hints);
return layout;
}
static void
gtk_popover_move_resize (GtkPopover *popover)
present_popup (GtkPopover *popover)
{
GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover);
GtkRequisition req;
GdkPopupLayout *layout;
if (priv->surface)
{
gtk_widget_get_preferred_size (GTK_WIDGET (popover), NULL, &req);
gdk_surface_resize (priv->surface, req.width, req.height);
move_to_rect (popover);
}
layout = create_popup_layout (popover);
gtk_widget_get_preferred_size (GTK_WIDGET (popover), NULL, &req);
if (gdk_surface_present_popup (priv->surface,
req.width, req.height,
layout))
update_popover_layout (popover, layout);
}
static void
gtk_popover_native_check_resize (GtkNative *native)
{
GtkPopover *popover = GTK_POPOVER (native);
GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover);
GtkWidget *widget = GTK_WIDGET (popover);
if (!_gtk_widget_get_alloc_needed (widget))
gtk_widget_ensure_allocate (widget);
else if (gtk_widget_get_visible (widget))
{
gtk_popover_move_resize (popover);
if (priv->surface)
gtk_widget_allocate (GTK_WIDGET (popover),
gdk_surface_get_width (priv->surface),
gdk_surface_get_height (priv->surface),
-1, NULL);
}
present_popup (popover);
}
@ -436,7 +616,8 @@ surface_state_changed (GtkWidget *widget)
if (changed_mask & GDK_SURFACE_STATE_WITHDRAWN)
{
if (priv->state & GDK_SURFACE_STATE_WITHDRAWN)
if (priv->state & GDK_SURFACE_STATE_WITHDRAWN &&
gtk_widget_is_visible (widget))
gtk_widget_hide (widget);
}
}
@ -467,36 +648,13 @@ surface_event (GdkSurface *surface,
}
static void
surface_moved_to_rect (GdkSurface *surface,
GdkRectangle *flipped_rect,
GdkRectangle *final_rect,
gboolean flipped_x,
gboolean flipped_y,
GtkWidget *widget)
popup_layout_changed (GdkSurface *surface,
GtkWidget *widget)
{
GtkPopover *popover = GTK_POPOVER (widget);
GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover);
priv->final_rect = *final_rect;
switch (priv->position)
{
case GTK_POS_LEFT:
priv->final_position = flipped_x ? GTK_POS_RIGHT : GTK_POS_LEFT;
break;
case GTK_POS_RIGHT:
priv->final_position = flipped_x ? GTK_POS_LEFT : GTK_POS_RIGHT;
break;
case GTK_POS_TOP:
priv->final_position = flipped_y ? GTK_POS_BOTTOM : GTK_POS_TOP;
break;
case GTK_POS_BOTTOM:
priv->final_position = flipped_y ? GTK_POS_TOP : GTK_POS_BOTTOM;
break;
default:
g_assert_not_reached ();
break;
}
update_popover_layout (popover, gdk_popup_layout_ref (priv->layout));
}
static void
@ -612,7 +770,7 @@ gtk_popover_realize (GtkWidget *widget)
g_signal_connect_swapped (priv->surface, "size-changed", G_CALLBACK (surface_size_changed), widget);
g_signal_connect (priv->surface, "render", G_CALLBACK (surface_render), widget);
g_signal_connect (priv->surface, "event", G_CALLBACK (surface_event), widget);
g_signal_connect (priv->surface, "moved-to-rect", G_CALLBACK (surface_moved_to_rect), widget);
g_signal_connect (priv->surface, "popup-layout-changed", G_CALLBACK (popup_layout_changed), widget);
GTK_WIDGET_CLASS (gtk_popover_parent_class)->realize (widget);
@ -634,7 +792,7 @@ gtk_popover_unrealize (GtkWidget *widget)
g_signal_handlers_disconnect_by_func (priv->surface, surface_size_changed, widget);
g_signal_handlers_disconnect_by_func (priv->surface, surface_render, widget);
g_signal_handlers_disconnect_by_func (priv->surface, surface_event, widget);
g_signal_handlers_disconnect_by_func (priv->surface, surface_moved_to_rect, widget);
g_signal_handlers_disconnect_by_func (priv->surface, popup_layout_changed, widget);
gdk_surface_set_widget (priv->surface, NULL);
gdk_surface_destroy (priv->surface);
g_clear_object (&priv->surface);
@ -648,7 +806,7 @@ gtk_popover_show (GtkWidget *widget)
_gtk_widget_set_visible_flag (widget, TRUE);
gtk_widget_realize (widget);
gtk_popover_native_check_resize (GTK_NATIVE (widget));
present_popup (popover);
gtk_widget_map (widget);
if (priv->autohide)
@ -683,8 +841,8 @@ surface_transform_changed_cb (GtkWidget *widget,
GtkPopover *popover = user_data;
GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover);
move_to_rect (popover);
g_clear_pointer (&priv->arrow_render_node, gsk_render_node_unref);
if (priv->surface && gdk_surface_is_visible (priv->surface))
present_popup (popover);
return G_SOURCE_CONTINUE;
}
@ -696,8 +854,7 @@ gtk_popover_map (GtkWidget *widget)
GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover);
GtkWidget *child;
gdk_surface_show (priv->surface);
move_to_rect (popover);
present_popup (popover);
priv->surface_transform_changed_cb =
gtk_widget_add_surface_transform_changed_callback (priv->relative_to,
@ -755,6 +912,11 @@ gtk_popover_dispose (GObject *object)
static void
gtk_popover_finalize (GObject *object)
{
GtkPopover *popover = GTK_POPOVER (object);
GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover);
g_clear_pointer (&priv->layout, gdk_popup_layout_unref);
G_OBJECT_CLASS (gtk_popover_parent_class)->finalize (object);
}
@ -1096,8 +1258,6 @@ gtk_popover_size_allocate (GtkWidget *widget,
GtkAllocation child_alloc;
int tail_height = priv->has_arrow ? TAIL_HEIGHT : 0;
gtk_popover_move_resize (popover);
switch (priv->final_position)
{
case GTK_POS_TOP:
@ -1414,13 +1574,16 @@ gtk_popover_new (GtkWidget *relative_to)
}
static void
size_changed (GtkWidget *widget,
int width,
int height,
int baseline,
GtkPopover *popover)
relative_to_size_changed (GtkWidget *widget,
int width,
int height,
int baseline,
GtkPopover *popover)
{
gtk_popover_move_resize (popover);
GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover);
if (priv->surface && gdk_surface_is_visible (priv->surface))
present_popup (popover);
}
void
@ -1487,7 +1650,9 @@ gtk_popover_set_relative_to (GtkPopover *popover,
if (priv->relative_to)
{
g_signal_handlers_disconnect_by_func (priv->relative_to, size_changed, popover);
g_signal_handlers_disconnect_by_func (priv->relative_to,
relative_to_size_changed,
popover);
gtk_widget_unparent (GTK_WIDGET (popover));
}
@ -1496,7 +1661,7 @@ gtk_popover_set_relative_to (GtkPopover *popover,
if (priv->relative_to)
{
g_signal_connect_object (priv->relative_to, "size-allocate",
G_CALLBACK (size_changed), popover, 0);
G_CALLBACK (relative_to_size_changed), popover, 0);
gtk_css_node_set_parent (gtk_widget_get_css_node (GTK_WIDGET (popover)),
gtk_widget_get_css_node (relative_to));
gtk_widget_set_parent (GTK_WIDGET (popover), relative_to);

View File

@ -110,16 +110,34 @@ gtk_tooltip_window_native_get_surface_transform (GtkNative *native,
*y = margin.top + border.top + padding.top;
}
static void
move_to_rect (GtkTooltipWindow *window)
static GdkPopupLayout *
create_popup_layout (GtkTooltipWindow *window)
{
gdk_surface_move_to_rect (window->surface,
&window->rect,
window->rect_anchor,
window->surface_anchor,
window->anchor_hints,
window->dx,
window->dy);
GdkPopupLayout *layout;
layout = gdk_popup_layout_new (&window->rect,
window->rect_anchor,
window->surface_anchor);
gdk_popup_layout_set_anchor_hints (layout, window->anchor_hints);
gdk_popup_layout_set_offset (layout, window->dx, window->dy);
return layout;
}
static void
relayout_popup (GtkTooltipWindow *window)
{
GdkPopupLayout *layout;
if (!gtk_widget_get_visible (GTK_WIDGET (window)))
return;
layout = create_popup_layout (window);
gdk_surface_present_popup (window->surface,
gdk_surface_get_width (window->surface),
gdk_surface_get_height (window->surface),
layout);
gdk_popup_layout_unref (layout);
}
static void
@ -131,7 +149,8 @@ gtk_tooltip_window_move_resize (GtkTooltipWindow *window)
{
gtk_widget_get_preferred_size (GTK_WIDGET (window), NULL, &req);
gdk_surface_resize (window->surface, req.width, req.height);
move_to_rect (window);
relayout_popup (window);
}
}
@ -206,16 +225,6 @@ surface_event (GdkSurface *surface,
return TRUE;
}
static void
surface_moved_to_rect (GdkSurface *surface,
GdkRectangle *flipped_rect,
GdkRectangle *final_rect,
gboolean flipped_x,
gboolean flipped_y,
GtkWidget *widget)
{
}
static void
gtk_tooltip_window_realize (GtkWidget *widget)
{
@ -234,7 +243,6 @@ gtk_tooltip_window_realize (GtkWidget *widget)
g_signal_connect_swapped (window->surface, "size-changed", G_CALLBACK (surface_size_changed), widget);
g_signal_connect (window->surface, "render", G_CALLBACK (surface_render), widget);
g_signal_connect (window->surface, "event", G_CALLBACK (surface_event), widget);
g_signal_connect (window->surface, "moved-to-rect", G_CALLBACK (surface_moved_to_rect), widget);
GTK_WIDGET_CLASS (gtk_tooltip_window_parent_class)->realize (widget);
@ -255,7 +263,6 @@ gtk_tooltip_window_unrealize (GtkWidget *widget)
g_signal_handlers_disconnect_by_func (window->surface, surface_size_changed, widget);
g_signal_handlers_disconnect_by_func (window->surface, surface_render, widget);
g_signal_handlers_disconnect_by_func (window->surface, surface_event, widget);
g_signal_handlers_disconnect_by_func (window->surface, surface_moved_to_rect, widget);
gdk_surface_set_widget (window->surface, NULL);
gdk_surface_destroy (window->surface);
g_clear_object (&window->surface);
@ -277,7 +284,7 @@ surface_transform_changed_cb (GtkWidget *widget,
{
GtkTooltipWindow *window = GTK_TOOLTIP_WINDOW (widget);
move_to_rect (window);
relayout_popup (window);
return G_SOURCE_CONTINUE;
}
@ -287,10 +294,15 @@ static void
gtk_tooltip_window_map (GtkWidget *widget)
{
GtkTooltipWindow *window = GTK_TOOLTIP_WINDOW (widget);
GdkPopupLayout *layout;
GtkWidget *child;
gdk_surface_show (window->surface);
move_to_rect (window);
layout = create_popup_layout (window);
gdk_surface_present_popup (window->surface,
gdk_surface_get_width (window->surface),
gdk_surface_get_height (window->surface),
layout);
gdk_popup_layout_unref (layout);
window->surface_transform_changed_cb =
gtk_widget_add_surface_transform_changed_callback (window->relative_to,
@ -599,6 +611,6 @@ gtk_tooltip_window_position (GtkTooltipWindow *window,
window->dx = dx;
window->dy = dy;
move_to_rect (window);
relayout_popup (window);
}