Merge branch 'dragsurface-resize' into 'main'

Fix drag surfaces ignoring size updates on Wayland and X11

See merge request GNOME/gtk!5597
This commit is contained in:
Matthias Clasen
2023-03-15 02:59:19 +00:00
12 changed files with 375 additions and 1 deletions

View File

@@ -45,6 +45,7 @@
#include <gdk/gdkdisplaymanager.h>
#include <gdk/gdkdrag.h>
#include <gdk/gdkdragsurface.h>
#include <gdk/gdkdragsurfacesize.h>
#include <gdk/gdkdrawcontext.h>
#include <gdk/gdkdrop.h>
#include <gdk/gdkenums.h>

View File

@@ -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);
}
/**

View File

@@ -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__ */

53
gdk/gdkdragsurfacesize.c Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
*
*/
#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;
}

42
gdk/gdkdragsurfacesize.h Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#if !defined(__GDK_H_INSIDE__) && !defined(GTK_COMPILATION)
#error "Only <gdk/gdk.h> can be included directly."
#endif
#include <gdk/gdktypes.h>
#include <gdk/gdkversionmacros.h>
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

View File

@@ -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 <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#include "gdkdragsurfacesize.h"
struct _GdkDragSurfaceSize
{
int width;
int height;
};
void gdk_drag_surface_size_init (GdkDragSurfaceSize *size);

View File

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

View File

@@ -33,6 +33,7 @@
#include "gdksurfaceprivate.h"
#include "gdktoplevelprivate.h"
#include "gdkdevice-wayland-private.h"
#include "gdkdragsurfacesizeprivate.h"
#include <wayland/xdg-shell-unstable-v6-client-protocol.h>
#include <wayland/xdg-foreign-unstable-v2-client-protocol.h>
@@ -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,

View File

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

View File

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

View File

@@ -31,6 +31,7 @@ gtk_tests = [
['testdialog'],
['testdnd'],
['testdnd2'],
['testdndresize'],
['testellipsise'],
['testentrycompletion'],
['testentryicons'],

123
tests/testdndresize.c Normal file
View File

@@ -0,0 +1,123 @@
#include "gtkcssprovider.h"
#include <gtk/gtk.h>
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;
}