Compare commits

...

10 Commits

Author SHA1 Message Date
Matthias Clasen
4dafd10201 x11: Update for api change 2023-03-08 14:08:26 -05:00
Matthias Clasen
58af693e79 wayland: Update for api change 2023-03-08 14:08:10 -05:00
Matthias Clasen
b4bb8c61a6 Update GdkDragSurface to use the size struct 2023-03-08 14:07:54 -05:00
Matthias Clasen
e7df61455b Add GdkDragSurfaceSize 2023-03-08 14:01:10 -05:00
Matthias Clasen
e5e08cc0d7 wip: Add a drag surface resize demo
Make the one of the image sources in the clipboard
demo have a throbbing drag icon.
2023-03-07 07:21:32 -05:00
Ivan Molodetskikh
eb828a5d76 x11/surface: Get current drag surface size with compute-size signal
Query and update size of drag surfaces, similarly to how it's done for
the Wayland backend in the previous commit.
2023-03-06 21:04:46 -08:00
Ivan Molodetskikh
53bd9f5443 wayland/dragsurface: Get current size with compute-size signal
GdkDragSurface-backed widgets are not parented to an existing widget,
unlike popovers, and like toplevels. This means that there's nobody to
actively call gdk_drag_surface_present() to update the size, and
GdkDragSurface should do it on its own, just like GdkToplevel.

This commit implements this for the Wayland backend.
2023-03-06 21:04:46 -08:00
Ivan Molodetskikh
fedb7a8809 dragicon: Handle compute-size
Compute our size when requested by the backend. This makes GtkDragIcons
actually recompute their size when it changes, instead of getting stuck
with the first size and potentially underallocating.
2023-03-06 21:04:46 -08:00
Ivan Molodetskikh
9dcc24e7bf dragsurface: Add compute-size signal
Similarly to GdkToplevel, GdkDragSurface's compute-size should be called
by backends to query the current surface size, and should be connected
to by widget implementations (like GtkDragIcon) to report the current
size.

GdkDragSurface-backed widgets are not parented to an existing widget,
unlike popovers, and like toplevels. This means that there's nobody to
actively call gdk_drag_surface_present() to update the size, and
GdkDragSurface should do it on its own, just like GdkToplevel.
2023-03-06 21:04:46 -08:00
Ivan Molodetskikh
eaf79cb1f0 tests: Add resizing drag icon test
The test can verify that dynamic drag icon resizing and hotspot
adjustment work as intended.
2023-03-06 21:04:46 -08:00
16 changed files with 587 additions and 1 deletions

View File

@ -12,6 +12,7 @@
#include <gtk/gtk.h>
#include <string.h>
#include "demoimage.h"
#include "gtkthrobber.h"
static void
copy_button_clicked (GtkStack *source_stack,
@ -364,6 +365,26 @@ drag_prepare (GtkDragSource *drag_source,
return gdk_content_provider_new_for_value (&value);
}
static void
drag_begin (GtkDragSource *drag_source,
GdkDrag *drag,
gpointer user_data)
{
GtkWidget *button;
GdkFrameClock *clock;
GdkPaintable *paintable;
button = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (drag_source));
clock = gtk_widget_get_frame_clock (button);
paintable = gtk_throbber_new (clock,
"/transparent/portland-rose.jpg",
0.02, 0.1,
drag);
gtk_drag_source_set_icon (drag_source, paintable, 10, 10);
g_object_unref (paintable);
}
GtkWidget *
do_clipboard (GtkWidget *do_widget)
{
@ -384,6 +405,7 @@ do_clipboard (GtkWidget *do_widget)
gtk_builder_cscope_add_callback (scope, open_folder_cb);
gtk_builder_cscope_add_callback (scope, on_drop);
gtk_builder_cscope_add_callback (scope, drag_prepare);
gtk_builder_cscope_add_callback (scope, drag_begin);
builder = gtk_builder_new ();
gtk_builder_set_scope (builder, scope);
gtk_builder_add_from_resource (builder, "/clipboard/clipboard.ui", NULL);

View File

@ -91,6 +91,7 @@
<child>
<object class="GtkDragSource">
<signal name="prepare" handler="drag_prepare"/>
<signal name="drag-begin" handler="drag_begin"/>
</object>
</child>
<child>

View File

@ -0,0 +1,174 @@
/*
* Copyright © 2018 Benjamin Otte
*
* 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.1 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/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "gtkthrobber.h"
#include "gtk.h"
struct _GtkThrobber
{
GObject parent_instance;
GdkFrameClock *clock;
GdkTexture *texture;
guint clock_tick_id;
float min, max, scale, delta;
float delta2, hot;
GdkDrag *drag;
};
struct _GtkThrobberClass
{
GObjectClass parent_class;
};
static void
gtk_throbber_paintable_snapshot (GdkPaintable *paintable,
GdkSnapshot *snapshot,
double width,
double height)
{
GtkThrobber *self = GTK_THROBBER (paintable);
gtk_snapshot_append_texture (snapshot, self->texture, &GRAPHENE_RECT_INIT (0, 0, width, height));
}
static int
gtk_throbber_paintable_get_intrinsic_width (GdkPaintable *paintable)
{
GtkThrobber *self = GTK_THROBBER (paintable);
return gdk_texture_get_width (self->texture) * self->scale;
}
static int
gtk_throbber_paintable_get_intrinsic_height (GdkPaintable *paintable)
{
GtkThrobber *self = GTK_THROBBER (paintable);
return gdk_texture_get_height (self->texture) * self->scale;
}
static void
gtk_throbber_paintable_init (GdkPaintableInterface *iface)
{
iface->snapshot = gtk_throbber_paintable_snapshot;
iface->get_intrinsic_width = gtk_throbber_paintable_get_intrinsic_width;
iface->get_intrinsic_height = gtk_throbber_paintable_get_intrinsic_height;
}
G_DEFINE_TYPE_EXTENDED (GtkThrobber, gtk_throbber, G_TYPE_OBJECT, 0,
G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
gtk_throbber_paintable_init))
static void
gtk_throbber_dispose (GObject *object)
{
GtkThrobber *self = GTK_THROBBER (object);
gdk_frame_clock_end_updating (self->clock);
g_signal_handler_disconnect (self->clock, self->clock_tick_id);
self->clock_tick_id = 0;
g_clear_object (&self->clock);
g_clear_object (&self->texture);
g_clear_object (&self->drag);
G_OBJECT_CLASS (gtk_throbber_parent_class)->dispose (object);
}
static void
gtk_throbber_class_init (GtkThrobberClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->dispose = gtk_throbber_dispose;
}
static void
gtk_throbber_init (GtkThrobber *self)
{
}
static void
on_frame_clock_update (GdkFrameClock *clock,
GtkThrobber *self)
{
self->scale += self->delta;
if (self->scale >= self->max)
{
self->scale = self->max;
self->delta = - self->delta;
}
else if (self->scale <= self->min)
{
self->scale = self->min;
self->delta = - self->delta;
}
self->hot += self->delta2;
if (self->hot >= 1.)
{
self->hot = 1.;
self->delta2 = - self->delta2;
}
else if (self->hot <= 0.)
{
self->hot = 0.;
self->delta2 = - self->delta2;
}
gdk_paintable_invalidate_size (GDK_PAINTABLE (self));
gdk_drag_set_hotspot (self->drag,
gdk_texture_get_width (self->texture) * self->scale * self->hot,
gdk_texture_get_height (self->texture) * self->scale * self->hot);
}
GdkPaintable *
gtk_throbber_new (GdkFrameClock *clock,
const char *resource_path,
float min,
float max,
GdkDrag *drag)
{
GtkThrobber *self;
self = g_object_new (GTK_TYPE_THROBBER, NULL);
self->clock = g_object_ref (clock);
self->texture = gdk_texture_new_from_resource (resource_path);
self->drag = g_object_ref (drag);
self->min = min;
self->max = max;
self->scale = min;
self->delta = (max - min) / (15 * 16);
self->delta2 = 1. / (10 * 16);
self->hot = 0.;
self->clock_tick_id = g_signal_connect (self->clock, "update",
G_CALLBACK (on_frame_clock_update), self);
gdk_frame_clock_begin_updating (self->clock);
return GDK_PAINTABLE (self);
}

View File

@ -0,0 +1,36 @@
/*
* Copyright © 2023 Matthias Clasen
*
* 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.1 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/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#pragma once
#include <gdk/gdk.h>
G_BEGIN_DECLS
#define GTK_TYPE_THROBBER (gtk_throbber_get_type ())
G_DECLARE_FINAL_TYPE (GtkThrobber, gtk_throbber, GTK, THROBBER, GObject)
GdkPaintable * gtk_throbber_new (GdkFrameClock *clock,
const char *resource_path,
float min,
float max,
GdkDrag *drag);
G_END_DECLS

View File

@ -134,6 +134,7 @@ extra_demo_sources = files([
'unicode-names.c',
'suggestionentry.c',
'language-names.c',
'gtkthrobber.c',
])
if os_unix

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,16 @@ static void
gdk_drag_surface_default_init (GdkDragSurfaceInterface *iface)
{
iface->present = gdk_drag_surface_default_present;
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_10
GType gdk_drag_surface_size_get_type (void);
GDK_AVAILABLE_IN_4_10
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',

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,14 @@ gdk_wayland_drag_surface_compute_size (GdkSurface *surface)
if (impl->next_layout.surface_geometry_dirty)
{
GdkDragSurfaceSize size;
gdk_drag_surface_size_init (&size);
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,37 @@ 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;
int new_width, new_height;
gdk_drag_surface_size_init (&size);
gdk_drag_surface_notify_compute_size (GDK_DRAG_SURFACE (surface), &size);
new_width = size.width;
new_height = size.height;
if ((impl->last_computed_width != new_width ||
impl->last_computed_height != new_height) &&
(impl->next_layout.configured_width != new_width ||
impl->next_layout.configured_height != new_height))
{
*width = new_width;
*height = new_height;
impl->last_computed_width = new_width;
impl->last_computed_height = new_height;
return TRUE;
}
return FALSE;
}
static gboolean
compute_size_idle (gpointer user_data)
{
@ -394,6 +426,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

@ -183,6 +183,18 @@ surface_render (GdkSurface *surface,
return TRUE;
}
static void
surface_compute_size (GdkDragSurface *surface,
int *width,
int *height,
GtkWidget *widget)
{
GtkRequisition size;
gtk_widget_get_preferred_size (widget, NULL, &size);
*width = size.width;
*height = size.height;
}
static void
gtk_drag_icon_realize (GtkWidget *widget)
{
@ -193,6 +205,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 +229,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;
}