diff --git a/gdk/gdk.h b/gdk/gdk.h index af43c7f175..4e8f31880d 100644 --- a/gdk/gdk.h +++ b/gdk/gdk.h @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include diff --git a/gdk/gdkdragsurface.c b/gdk/gdkdragsurface.c index 97c3e7dd36..dcb6c28cca 100644 --- a/gdk/gdkdragsurface.c +++ b/gdk/gdkdragsurface.c @@ -37,6 +37,22 @@ G_DEFINE_INTERFACE (GdkDragSurface, gdk_drag_surface, GDK_TYPE_SURFACE) +enum +{ + COMPUTE_SIZE, + + N_SIGNALS +}; + +static guint signals[N_SIGNALS] = { 0 }; + +void +gdk_drag_surface_notify_compute_size (GdkDragSurface *surface, + GdkDragSurfaceSize *size) +{ + g_signal_emit (surface, signals[COMPUTE_SIZE], 0, size); +} + static gboolean gdk_drag_surface_default_present (GdkDragSurface *drag_surface, int width, @@ -49,6 +65,35 @@ static void gdk_drag_surface_default_init (GdkDragSurfaceInterface *iface) { iface->present = gdk_drag_surface_default_present; + + /** + * GdkDragSurface::compute-size: + * @surface: a `GdkDragSurface` + * @size: (type Gdk.DragSurfaceSize) (out caller-allocates): a + * `GdkDragSurfaceSize` + * + * Emitted when the size for the surface needs to be computed, when it is + * present. + * + * It will normally be emitted during the native surface layout cycle when the + * surface size needs to be recomputed. + * + * It is the responsibility of the drag surface user to handle this signal and + * compute the desired size of the surface, storing the computed size in the + * [struct@Gdk.DragSurfaceSize] object. Failing to do so will result in an + * arbitrary size being used as a result. + * + * Since: 4.12 + */ + signals[COMPUTE_SIZE] = + g_signal_new (I_("compute-size"), + GDK_TYPE_DRAG_SURFACE, + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + NULL, + G_TYPE_NONE, 1, + GDK_TYPE_DRAG_SURFACE_SIZE); } /** diff --git a/gdk/gdkdragsurfaceprivate.h b/gdk/gdkdragsurfaceprivate.h index e32cfd780c..d565b8418d 100644 --- a/gdk/gdkdragsurfaceprivate.h +++ b/gdk/gdkdragsurfaceprivate.h @@ -2,6 +2,7 @@ #define __GDK_DRAG_SURFACE_PRIVATE_H__ #include "gdkdragsurface.h" +#include "gdkdragsurfacesize.h" G_BEGIN_DECLS @@ -15,6 +16,9 @@ struct _GdkDragSurfaceInterface int height); }; +void gdk_drag_surface_notify_compute_size (GdkDragSurface *surface, + GdkDragSurfaceSize *size); + G_END_DECLS #endif /* __GDK_DRAG_SURFACE_PRIVATE_H__ */ diff --git a/gdk/gdkdragsurfacesize.c b/gdk/gdkdragsurfacesize.c new file mode 100644 index 0000000000..354d3ecc1c --- /dev/null +++ b/gdk/gdkdragsurfacesize.c @@ -0,0 +1,53 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 2023 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 . + * + */ + +#include "config.h" + +#include "gdkdragsurfacesizeprivate.h" + +/** + * GdkDragSurfaceSize: + * + * The `GdkDragSurfaceSize` struct contains information that is useful + * to compute the size of a drag surface. + */ + +G_DEFINE_POINTER_TYPE (GdkDragSurfaceSize, gdk_drag_surface_size) + +void +gdk_drag_surface_size_init (GdkDragSurfaceSize *size) +{ + *size = (GdkDragSurfaceSize) { 0 }; +} + +/** + * gdk_drag_surface_size_set_size: + * @size: a `GdkDragSurfaceSize` + * @width: the width + * @height: the height + * + * Sets the size the drag surface prefers to be resized to. + */ +void +gdk_drag_surface_size_set_size (GdkDragSurfaceSize *size, + int width, + int height) +{ + size->width = width; + size->height = height; +} diff --git a/gdk/gdkdragsurfacesize.h b/gdk/gdkdragsurfacesize.h new file mode 100644 index 0000000000..28d23128be --- /dev/null +++ b/gdk/gdkdragsurfacesize.h @@ -0,0 +1,42 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 2023 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 . + * + */ + +#pragma once + +#if !defined(__GDK_H_INSIDE__) && !defined(GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include + +G_BEGIN_DECLS + +typedef struct _GdkDragSurfaceSize GdkDragSurfaceSize; + +#define GDK_TYPE_DRAG_SURFACE_SIZE (gdk_drag_surface_size_get_type ()) + +GDK_AVAILABLE_IN_4_12 +GType gdk_drag_surface_size_get_type (void); + +GDK_AVAILABLE_IN_4_12 +void gdk_drag_surface_size_set_size (GdkDragSurfaceSize *size, + int width, + int height); + +G_END_DECLS diff --git a/gdk/gdkdragsurfacesizeprivate.h b/gdk/gdkdragsurfacesizeprivate.h new file mode 100644 index 0000000000..f777f473c8 --- /dev/null +++ b/gdk/gdkdragsurfacesizeprivate.h @@ -0,0 +1,29 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 2023 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 . + * + */ + +#pragma once + +#include "gdkdragsurfacesize.h" + +struct _GdkDragSurfaceSize +{ + int width; + int height; +}; + +void gdk_drag_surface_size_init (GdkDragSurfaceSize *size); diff --git a/gdk/meson.build b/gdk/meson.build index 3f97ac16fb..0e97663c08 100644 --- a/gdk/meson.build +++ b/gdk/meson.build @@ -16,6 +16,8 @@ gdk_public_sources = files([ 'gdkdisplay.c', 'gdkdisplaymanager.c', 'gdkdrag.c', + 'gdkdragsurface.c', + 'gdkdragsurfacesize.c', 'gdkdrawcontext.c', 'gdkdrop.c', 'gdkevents.c', @@ -52,7 +54,6 @@ gdk_public_sources = files([ 'gdktoplevellayout.c', 'gdktoplevelsize.c', 'gdktoplevel.c', - 'gdkdragsurface.c', 'loaders/gdkpng.c', 'loaders/gdktiff.c', 'loaders/gdkjpeg.c', @@ -76,6 +77,7 @@ gdk_public_headers = files([ 'gdkdisplay.h', 'gdkdisplaymanager.h', 'gdkdrag.h', + 'gdkdragsurfacesize.h', 'gdkdrawcontext.h', 'gdkdrop.h', 'gdkenums.h', diff --git a/gdk/wayland/gdkdragsurface-wayland.c b/gdk/wayland/gdkdragsurface-wayland.c index fb42488970..22220a49e4 100644 --- a/gdk/wayland/gdkdragsurface-wayland.c +++ b/gdk/wayland/gdkdragsurface-wayland.c @@ -33,6 +33,7 @@ #include "gdksurfaceprivate.h" #include "gdktoplevelprivate.h" #include "gdkdevice-wayland-private.h" +#include "gdkdragsurfacesizeprivate.h" #include #include @@ -78,6 +79,17 @@ gdk_wayland_drag_surface_compute_size (GdkSurface *surface) if (impl->next_layout.surface_geometry_dirty) { + GdkDragSurfaceSize size; + + gdk_drag_surface_size_init (&size); + size.width = impl->next_layout.configured_width; + size.height = impl->next_layout.configured_height; + + gdk_drag_surface_notify_compute_size (GDK_DRAG_SURFACE (surface), &size); + + impl->next_layout.configured_width = size.width; + impl->next_layout.configured_height = size.height; + gdk_wayland_surface_update_size (surface, impl->next_layout.configured_width, impl->next_layout.configured_height, diff --git a/gdk/x11/gdksurface-x11.c b/gdk/x11/gdksurface-x11.c index 044a1185fa..8f56fe5e74 100644 --- a/gdk/x11/gdksurface-x11.c +++ b/gdk/x11/gdksurface-x11.c @@ -40,6 +40,7 @@ #include "gdkglcontext-x11.h" #include "gdkprivate-x11.h" #include "gdktextureprivate.h" +#include "gdkdragsurfacesizeprivate.h" #include "gdkseatprivate.h" #include "gdkprivate.h" @@ -345,6 +346,36 @@ compute_toplevel_size (GdkSurface *surface, return FALSE; } +static gboolean +compute_drag_surface_size (GdkSurface *surface, + int *width, + int *height) +{ + GdkX11Surface *impl = GDK_X11_SURFACE (surface); + GdkDragSurfaceSize size; + + gdk_drag_surface_size_init (&size); + size.width = impl->next_layout.configured_width; + size.height = impl->next_layout.configured_height; + + gdk_drag_surface_notify_compute_size (GDK_DRAG_SURFACE (surface), &size); + + if ((impl->last_computed_width != size.width || + impl->last_computed_height != size.height) && + (impl->next_layout.configured_width != size.width || + impl->next_layout.configured_height != size.height)) + { + *width = size.width; + *height = size.height; + impl->last_computed_width = size.width; + impl->last_computed_height = size.height; + + return TRUE; + } + + return FALSE; +} + static gboolean compute_size_idle (gpointer user_data) { @@ -394,6 +425,24 @@ gdk_x11_surface_compute_size (GdkSurface *surface) impl->surface_scale); } + impl->next_layout.surface_geometry_dirty = FALSE; + impl->next_layout.configure_pending = FALSE; + } + else if (GDK_IS_DRAG_SURFACE (surface)) + { + int width, height; + + if (compute_drag_surface_size (surface, &width, &height)) + gdk_x11_surface_toplevel_resize (surface, width, height); + + if (surface->resize_count == 0) + { + gdk_x11_surface_update_size (impl, + impl->next_layout.configured_width, + impl->next_layout.configured_height, + impl->surface_scale); + } + impl->next_layout.surface_geometry_dirty = FALSE; impl->next_layout.configure_pending = FALSE; } diff --git a/gtk/gtkdragicon.c b/gtk/gtkdragicon.c index 3d684ecfbc..4035725e25 100644 --- a/gtk/gtkdragicon.c +++ b/gtk/gtkdragicon.c @@ -28,6 +28,7 @@ #include "gtkcssnumbervalueprivate.h" #include "gdk/gdksurfaceprivate.h" +#include "gdk/gdkdragsurfacesize.h" /* for the drag icons */ #include "gtkcolorswatchprivate.h" @@ -183,6 +184,16 @@ surface_render (GdkSurface *surface, return TRUE; } +static void +surface_compute_size (GdkDragSurface *surface, + GdkDragSurfaceSize *size, + GtkWidget *widget) +{ + GtkRequisition nat_size; + gtk_widget_get_preferred_size (widget, NULL, &nat_size); + gdk_drag_surface_size_set_size (size, nat_size.width, nat_size.height); +} + static void gtk_drag_icon_realize (GtkWidget *widget) { @@ -193,6 +204,7 @@ gtk_drag_icon_realize (GtkWidget *widget) gdk_surface_set_widget (icon->surface, widget); g_signal_connect (icon->surface, "render", G_CALLBACK (surface_render), widget); + g_signal_connect (icon->surface, "compute-size", G_CALLBACK (surface_compute_size), widget); GTK_WIDGET_CLASS (gtk_drag_icon_parent_class)->realize (widget); @@ -216,6 +228,7 @@ gtk_drag_icon_unrealize (GtkWidget *widget) if (icon->surface) { g_signal_handlers_disconnect_by_func (icon->surface, surface_render, widget); + g_signal_handlers_disconnect_by_func (icon->surface, surface_compute_size, widget); gdk_surface_set_widget (icon->surface, NULL); } } diff --git a/tests/meson.build b/tests/meson.build index 10c645dade..de23da8132 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -31,6 +31,7 @@ gtk_tests = [ ['testdialog'], ['testdnd'], ['testdnd2'], + ['testdndresize'], ['testellipsise'], ['testentrycompletion'], ['testentryicons'], diff --git a/tests/testdndresize.c b/tests/testdndresize.c new file mode 100644 index 0000000000..39bcbcdc5b --- /dev/null +++ b/tests/testdndresize.c @@ -0,0 +1,123 @@ +#include "gtkcssprovider.h" +#include + +static GtkRequisition size; +static gint64 start_time; +static gboolean stop_update_size; + +static gboolean +update_size (GtkWidget *widget, GdkFrameClock *clock, gpointer data) +{ + GdkDrag *drag = data; + gint64 now = g_get_monotonic_time (); + float t; + int width, height; + + if (stop_update_size) + return G_SOURCE_REMOVE; + + t = fmodf ((now - start_time) / (float) G_TIME_SPAN_SECOND, 1); + if (t >= 0.5) + t = 1 - t; + + width = size.width + t * 300; + height = size.height + t * 150; + + gtk_widget_set_size_request (widget, width, height); + gdk_drag_set_hotspot (drag, width / 2, height / 2); + + return G_SOURCE_CONTINUE; +} + +static void +drag_begin (GtkDragSource *source, + GdkDrag *drag) +{ + GtkWidget *widget; + GtkWidget *icon; + + icon = gtk_drag_icon_get_for_drag (drag); + + widget = gtk_label_new ("This Should Resize\n\nAnd Stay Centered"); + gtk_widget_add_css_class (widget, "dnd"); + gtk_widget_get_preferred_size (widget, NULL, &size); + + gtk_drag_icon_set_child (GTK_DRAG_ICON (icon), widget); + gtk_widget_set_size_request (widget, size.width, size.height); + gdk_drag_set_hotspot (drag, size.width / 2, size.height / 2); + + start_time = g_get_monotonic_time (); + stop_update_size = FALSE; + gtk_widget_add_tick_callback (widget, update_size, drag, NULL); +} + +static void +drag_end (GtkDragSource *source, + GdkDrag *drag, + gboolean delete_data, + gboolean data) +{ + stop_update_size = TRUE; +} + +static void +quit_cb (GtkWidget *widget, + gpointer data) +{ + gboolean *done = data; + + *done = TRUE; + + g_main_context_wakeup (NULL); +} + +int +main (int argc, char *argv[]) +{ + GtkCssProvider *provider; + GtkWidget *window; + GtkWidget *label; + GtkDragSource *source; + GdkContentProvider *content; + gboolean done = FALSE; + + gtk_init (); + + provider = gtk_css_provider_new (); + gtk_css_provider_load_from_data (provider, + ".dnd {" + "background-color: red;" + "border-top: 10px solid rebeccapurple;" + "}", + -1); + gtk_style_context_add_provider_for_display (gdk_display_get_default (), + GTK_STYLE_PROVIDER(provider), + GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + + window = gtk_window_new (); + g_signal_connect (window, "destroy", G_CALLBACK (quit_cb), &done); + + label = gtk_label_new ("Drag Me"); + g_object_set (label, + "margin-start", 64, + "margin-end", 64, + "margin-top", 64, + "margin-bottom", 64, + NULL); + + source = gtk_drag_source_new (); + content = gdk_content_provider_new_typed (G_TYPE_STRING, "I'm data!"); + gtk_drag_source_set_content (source, content); + g_signal_connect (source, "drag-begin", G_CALLBACK (drag_begin), NULL); + g_signal_connect (source, "drag-end", G_CALLBACK (drag_end), NULL); + gtk_widget_add_controller (label, GTK_EVENT_CONTROLLER (source)); + + gtk_window_set_child (GTK_WINDOW (window), label); + + gtk_window_present (GTK_WINDOW (window)); + + while (!done) + g_main_context_iteration (NULL, TRUE); + + return 0; +}