diff --git a/gdk/wayland/gdkwindow-wayland.c b/gdk/wayland/gdkwindow-wayland.c index 7dff256914..1e8863311f 100644 --- a/gdk/wayland/gdkwindow-wayland.c +++ b/gdk/wayland/gdkwindow-wayland.c @@ -1227,36 +1227,27 @@ should_be_mapped (GdkWindow *window) return TRUE; } -static gboolean -should_map_as_subsurface (GdkWindow *window) -{ - GdkWindowImplWayland *impl = GDK_WINDOW_IMPL_WAYLAND (window->impl); - - if (GDK_WINDOW_TYPE (window) == GDK_WINDOW_SUBSURFACE) - return TRUE; - - switch (impl->hint) - { - case GDK_WINDOW_TYPE_HINT_TOOLTIP: - return TRUE; - - case GDK_WINDOW_TYPE_HINT_UTILITY: - if (GDK_WINDOW_TYPE (window) == GDK_WINDOW_TEMP) - return TRUE; - break; - - default: - break; - } - - return FALSE; -} - static gboolean should_map_as_popup (GdkWindow *window) { GdkWindowImplWayland *impl = GDK_WINDOW_IMPL_WAYLAND (window->impl); + /* Ideally, popup would be temp windows with a parent and grab */ + if (GDK_WINDOW_TYPE (window) == GDK_WINDOW_TEMP) + { + /* If a temp window has a parent and a grab, we can use a popup */ + if (impl->transient_for) + { + if (impl->grab_input_seat) + return TRUE; + } + else + g_warning ("Window %p is a temporary window without parent, " + "application will not be able to position it on screen.", + window); + } + + /* Yet we need to keep the window type hint tests for compatibility */ switch (impl->hint) { case GDK_WINDOW_TYPE_HINT_POPUP_MENU: @@ -1276,6 +1267,38 @@ should_map_as_popup (GdkWindow *window) return FALSE; } +static gboolean +should_map_as_subsurface (GdkWindow *window) +{ + GdkWindowImplWayland *impl = GDK_WINDOW_IMPL_WAYLAND (window->impl); + + if (GDK_WINDOW_TYPE (window) == GDK_WINDOW_SUBSURFACE) + return TRUE; + + if (GDK_WINDOW_TYPE (window) != GDK_WINDOW_TEMP) + return FALSE; + + /* if we want a popup, we do not want a subsurface */ + if (should_map_as_popup (window)) + return FALSE; + + if (impl->transient_for) + { + GdkWindowImplWayland *impl_parent; + + impl_parent = GDK_WINDOW_IMPL_WAYLAND (impl->transient_for->impl); + /* subsurface require that the parent is mapped */ + if (impl_parent->mapped) + return TRUE; + else + g_warning ("Couldn't map window %p as susburface because its parent is not mapped.", + window); + + } + + return FALSE; +} + /* Get the window that can be used as a parent for a popup, i.e. a xdg_surface * or xdg_popup. If the window is not, traverse up the transiency parents until * we find one. @@ -1313,7 +1336,7 @@ gdk_wayland_window_map (GdkWindow *window) if (impl->transient_for) gdk_wayland_window_create_subsurface (window); else - g_warning ("Couldn't map as window %p as susburface yet because it doesn't have a parent", + g_warning ("Couldn't map window %p as susburface yet because it doesn't have a parent", window); } else if (should_map_as_popup (window)) diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c index b929a9c618..8b4e86a87f 100644 --- a/gtk/gtkwindow.c +++ b/gtk/gtkwindow.c @@ -3229,6 +3229,12 @@ gtk_window_unset_transient_for (GtkWindow *window) * * Passing %NULL for @parent unsets the current transient window. * + * On Wayland, this function can also be used to attach a new + * #GTK_WINDOW_POPUP to a #GTK_WINDOW_TOPLEVEL parent already mapped + * on screen so that the #GTK_WINDOW_POPUP will be created as a + * subsurface-based window #GDK_WINDOW_SUBSURFACE which can be + * positioned at will relatively to the #GTK_WINDOW_TOPLEVEL surface. + * * On Windows, this function puts the child window on top of the parent, * much as the window manager would have done on X. */ diff --git a/tests/Makefile.am b/tests/Makefile.am index f2123d97db..85d06bae6c 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -170,6 +170,7 @@ noinst_PROGRAMS = $(TEST_PROGS) \ gdkgears \ listmodel \ foreigndrawing \ + testpopup \ $(NULL) if USE_X11 diff --git a/tests/testpopup.c b/tests/testpopup.c new file mode 100644 index 0000000000..cd82468c49 --- /dev/null +++ b/tests/testpopup.c @@ -0,0 +1,68 @@ +#include + +static gboolean +draw_popup (GtkWidget *widget, + cairo_t *cr, + gpointer data) +{ + cairo_set_source_rgb (cr, 1, 0, 0); + cairo_paint (cr); + + return FALSE; +} + +static gboolean +place_popup (GtkWidget *parent, + GdkEvent *event, + GtkWidget *popup) +{ + GdkEventMotion *ev_motion = (GdkEventMotion *) event; + gint width, height; + + gtk_window_get_size (GTK_WINDOW (popup), &width, &height); + gtk_window_move (GTK_WINDOW (popup), + (int) ev_motion->x_root - width / 2, + (int) ev_motion->y_root - height / 2); + + return FALSE; +} + +static gboolean +on_map_event (GtkWidget *parent, + GdkEvent *event, + gpointer data) +{ + GtkWidget *popup; + + popup = gtk_window_new (GTK_WINDOW_POPUP); + + gtk_widget_set_size_request (GTK_WIDGET (popup), 20, 20); + gtk_widget_set_app_paintable (GTK_WIDGET (popup), TRUE); + gtk_window_set_transient_for (GTK_WINDOW (popup), GTK_WINDOW (parent)); + g_signal_connect (popup, "draw", G_CALLBACK (draw_popup), NULL); + g_signal_connect (parent, "motion-notify-event", G_CALLBACK (place_popup), popup); + + gtk_widget_show (popup); + + return FALSE; +} + +int +main (int argc, char *argv[]) +{ + GtkWidget *window; + + gtk_init (&argc, &argv); + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + + gtk_widget_set_events (window, GDK_POINTER_MOTION_MASK); + g_signal_connect (window, "destroy", gtk_main_quit, NULL); + g_signal_connect (window, "map-event", G_CALLBACK (on_map_event), NULL); + + gtk_widget_show (window); + + gtk_main (); + + return 0; +}