diff --git a/gtk/gtkdragicon.c b/gtk/gtkdragicon.c new file mode 100644 index 0000000000..8b6edbbf97 --- /dev/null +++ b/gtk/gtkdragicon.c @@ -0,0 +1,398 @@ +/* GTK - The GIMP Toolkit + * Copyright 2019 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 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 "gtkdragiconprivate.h" + +#include "gtkprivate.h" +#include "gtkintl.h" +#include "gtkwidgetprivate.h" +#include "gtkcssnodeprivate.h" + + +struct _GtkDragIcon +{ + GtkWidget parent_instance; + + GdkSurface *surface; + GskRenderer *renderer; + GtkWidget *widget; +}; + +struct _GtkDragIconClass +{ + GtkWidgetClass parent_class; +}; + +enum { + LAST_ARG = 1 +}; + +static void gtk_drag_icon_root_init (GtkRootInterface *iface); +static void gtk_drag_icon_native_init (GtkNativeInterface *iface); + +G_DEFINE_TYPE_WITH_CODE (GtkDragIcon, gtk_drag_icon, GTK_TYPE_CONTAINER, + G_IMPLEMENT_INTERFACE (GTK_TYPE_NATIVE, + gtk_drag_icon_native_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_ROOT, + gtk_drag_icon_root_init)) + +static GdkDisplay * +gtk_drag_icon_root_get_display (GtkRoot *self) +{ + GtkDragIcon *icon = GTK_DRAG_ICON (self); + + if (icon->surface) + return gdk_surface_get_display (icon->surface); + + return gdk_display_get_default (); +} + +static void +gtk_drag_icon_root_init (GtkRootInterface *iface) +{ + iface->get_display = gtk_drag_icon_root_get_display; +} + +static GdkSurface * +gtk_drag_icon_native_get_surface (GtkNative *native) +{ + GtkDragIcon *icon = GTK_DRAG_ICON (native); + + return icon->surface; +} + +static GskRenderer * +gtk_drag_icon_native_get_renderer (GtkNative *native) +{ + GtkDragIcon *icon = GTK_DRAG_ICON (native); + + return icon->renderer; +} + +static void +gtk_drag_icon_native_get_surface_transform (GtkNative *native, + int *x, + int *y) +{ + GtkStyleContext *context; + GtkBorder margin, border, padding; + + context = gtk_widget_get_style_context (GTK_WIDGET (native)); + gtk_style_context_get_margin (context, &margin); + gtk_style_context_get_border (context, &border); + gtk_style_context_get_padding (context, &padding); + + *x = margin.left + border.left + padding.left; + *y = margin.top + border.top + padding.top; +} + +static void +gtk_drag_icon_move_resize (GtkDragIcon *icon) +{ + GtkRequisition req; + + if (icon->surface) + { + gtk_widget_get_preferred_size (GTK_WIDGET (icon), NULL, &req); + gdk_surface_resize (icon->surface, req.width, req.height); + } +} + +static void +gtk_drag_icon_native_check_resize (GtkNative *native) +{ + GtkDragIcon *icon = GTK_DRAG_ICON (native); + GtkWidget *widget = GTK_WIDGET (native); + + if (!_gtk_widget_get_alloc_needed (widget)) + gtk_widget_ensure_allocate (widget); + else if (gtk_widget_get_visible (widget)) + { + gtk_drag_icon_move_resize (icon); + if (icon->surface) + gtk_widget_allocate (widget, + gdk_surface_get_width (icon->surface), + gdk_surface_get_height (icon->surface), + -1, NULL); + } +} + +static void +gtk_drag_icon_native_init (GtkNativeInterface *iface) +{ + iface->get_surface = gtk_drag_icon_native_get_surface; + iface->get_renderer = gtk_drag_icon_native_get_renderer; + iface->get_surface_transform = gtk_drag_icon_native_get_surface_transform; + iface->check_resize = gtk_drag_icon_native_check_resize; +} + +static gboolean +surface_render (GdkSurface *surface, + cairo_region_t *region, + GtkWidget *widget) +{ + gtk_widget_render (widget, surface, region); + return TRUE; +} + +static void +gtk_drag_icon_realize (GtkWidget *widget) +{ + GtkDragIcon *icon = GTK_DRAG_ICON (widget); + + g_warn_if_fail (icon->surface != NULL); + + gdk_surface_set_widget (icon->surface, widget); + + g_signal_connect (icon->surface, "render", G_CALLBACK (surface_render), widget); + + GTK_WIDGET_CLASS (gtk_drag_icon_parent_class)->realize (widget); + + icon->renderer = gsk_renderer_new_for_surface (icon->surface); +} + +static void +gtk_drag_icon_unrealize (GtkWidget *widget) +{ + GtkDragIcon *icon = GTK_DRAG_ICON (widget); + + GTK_WIDGET_CLASS (gtk_drag_icon_parent_class)->unrealize (widget); + + gsk_renderer_unrealize (icon->renderer); + g_clear_object (&icon->renderer); + + if (icon->surface) + { + g_signal_handlers_disconnect_by_func (icon->surface, surface_render, widget); + gdk_surface_set_widget (icon->surface, NULL); + } +} + +static void +gtk_drag_icon_map (GtkWidget *widget) +{ + GtkDragIcon *icon = GTK_DRAG_ICON (widget); + + gdk_surface_show (icon->surface); + + GTK_WIDGET_CLASS (gtk_drag_icon_parent_class)->map (widget); + + if (icon->widget && gtk_widget_get_visible (icon->widget)) + gtk_widget_map (icon->widget); +} + +static void +gtk_drag_icon_unmap (GtkWidget *widget) +{ + GtkDragIcon *icon = GTK_DRAG_ICON (widget); + + g_warn_if_fail (icon->surface != NULL); + GTK_WIDGET_CLASS (gtk_drag_icon_parent_class)->unmap (widget); + if (icon->surface) + gdk_surface_hide (icon->surface); + + if (icon->widget) + gtk_widget_unmap (icon->widget); +} + +static void +gtk_drag_icon_measure (GtkWidget *widget, + GtkOrientation orientation, + int for_size, + int *minimum, + int *natural, + int *minimum_baseline, + int *natural_baseline) +{ + GtkDragIcon *icon = GTK_DRAG_ICON (widget); + + if (icon->widget) + gtk_widget_measure (icon->widget, + orientation, for_size, + minimum, natural, + minimum_baseline, natural_baseline); +} + +static void +gtk_drag_icon_size_allocate (GtkWidget *widget, + int width, + int height, + int baseline) +{ + GtkDragIcon *icon = GTK_DRAG_ICON (widget); + + gtk_drag_icon_move_resize (icon); + + if (icon->widget) + gtk_widget_allocate (icon->widget, width, height, baseline, NULL); +} + +static void +gtk_drag_icon_show (GtkWidget *widget) +{ + _gtk_widget_set_visible_flag (widget, TRUE); + gtk_css_node_validate (gtk_widget_get_css_node (widget)); + gtk_widget_realize (widget); + gtk_drag_icon_native_check_resize (GTK_NATIVE (widget)); + gtk_widget_map (widget); +} + +static void +gtk_drag_icon_hide (GtkWidget *widget) +{ + _gtk_widget_set_visible_flag (widget, FALSE); + gtk_widget_unmap (widget); +} + +static void +gtk_drag_icon_dispose (GObject *object) +{ + GtkDragIcon *icon = GTK_DRAG_ICON (object); + + g_clear_pointer (&icon->widget, gtk_widget_unparent); + + G_OBJECT_CLASS (gtk_drag_icon_parent_class)->dispose (object); + + g_clear_object (&icon->surface); +} + +static void +gtk_drag_icon_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) + { + case LAST_ARG + GTK_ROOT_PROP_FOCUS_WIDGET: + g_value_set_object (value, NULL); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_drag_icon_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) + { + case LAST_ARG + GTK_ROOT_PROP_FOCUS_WIDGET: + // do nothing + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_drag_icon_add (GtkContainer *self, + GtkWidget *widget) +{ + GtkDragIcon *icon = GTK_DRAG_ICON (self); + + if (icon->widget) + { + g_warning ("GtkDragIcon already has a child"); + return; + } + + gtk_widget_set_parent (widget, GTK_WIDGET (icon)); + icon->widget = widget; +} + +static void +gtk_drag_icon_remove (GtkContainer *self, + GtkWidget *widget) +{ + GtkDragIcon *icon = GTK_DRAG_ICON (self); + + if (icon->widget == widget) + { + gtk_widget_unparent (widget); + icon->widget = NULL; + } +} + +static void +gtk_drag_icon_class_init (GtkDragIconClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass); + + object_class->dispose = gtk_drag_icon_dispose; + object_class->get_property = gtk_drag_icon_get_property; + object_class->set_property = gtk_drag_icon_set_property; + + widget_class->realize = gtk_drag_icon_realize; + widget_class->unrealize = gtk_drag_icon_unrealize; + widget_class->map = gtk_drag_icon_map; + widget_class->unmap = gtk_drag_icon_unmap; + widget_class->measure = gtk_drag_icon_measure; + widget_class->size_allocate = gtk_drag_icon_size_allocate; + widget_class->show = gtk_drag_icon_show; + widget_class->hide = gtk_drag_icon_hide; + + container_class->add = gtk_drag_icon_add; + container_class->remove = gtk_drag_icon_remove; + + gtk_root_install_properties (object_class, LAST_ARG); + + gtk_widget_class_set_css_name (widget_class, "dnd"); +} + +static void +gtk_drag_icon_init (GtkDragIcon *self) +{ +} + +GtkWidget * +gtk_drag_icon_new (void) +{ + return g_object_new (GTK_TYPE_DRAG_ICON, NULL); +} + +void +gtk_drag_icon_set_surface (GtkDragIcon *icon, + GdkSurface *surface) +{ + g_set_object (&icon->surface, surface); +} + +void +gtk_drag_icon_set_widget (GtkDragIcon *icon, + GtkWidget *widget) +{ + if (icon->widget == widget) + return; + + if (icon->widget) + gtk_widget_unparent (icon->widget); + + icon->widget = widget; + + if (icon->widget) + gtk_widget_set_parent (icon->widget, GTK_WIDGET (icon)); +} diff --git a/gtk/gtkdragiconprivate.h b/gtk/gtkdragiconprivate.h new file mode 100644 index 0000000000..507ac908ea --- /dev/null +++ b/gtk/gtkdragiconprivate.h @@ -0,0 +1,46 @@ +/* GTK - The GIMP Toolkit + * Copyright 2019 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 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 . + */ + +/* + * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#ifndef __GTK_DRAG_ICON_PRIVATE_H__ +#define __GTK_DRAG_ICON_PRIVATE_H__ + +#include +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_DRAG_ICON (gtk_drag_icon_get_type ()) + +G_DECLARE_FINAL_TYPE (GtkDragIcon, gtk_drag_icon, GTK, DRAG_ICON, GtkContainer) + +GtkWidget * gtk_drag_icon_new (void); + +void gtk_drag_icon_set_surface (GtkDragIcon *icon, + GdkSurface *surface); +void gtk_drag_icon_set_widget (GtkDragIcon *icon, + GtkWidget *widget); + +G_END_DECLS + +#endif /* __GTK_DRAG_ICON_PRIVATE_H__ */ diff --git a/gtk/meson.build b/gtk/meson.build index 85a25212a6..d93c94ac60 100644 --- a/gtk/meson.build +++ b/gtk/meson.build @@ -103,6 +103,7 @@ gtk_private_sources = files([ 'gtkcssvalue.c', 'gtkcsswidgetnode.c', 'gtkcustomlayout.c', + 'gtkdragicon.c', 'gtkfilechooserembed.c', 'gtkfilechooserentry.c', 'gtkfilechoosererrorstack.c',