[gtk/wip/otte/dnd: 116/126] droptarget: Redo



commit a411959c910fbf86754b38ffcd7b9615d7788a74
Author: Benjamin Otte <otte redhat com>
Date:   Sat Feb 29 03:47:17 2020 +0100

    droptarget: Redo
    
    This is a huge reorganization of GtkDropTarget. I did not know how to
    split this up, so it's unfortunately all one commit.
    
    Highlights:
    
    - Split GtkDropTarget into GtkDropTarget and GtkDropTargetAsync
      GtkDropTarget is the simple one that only works with GTypes and offers
      a synchronous interface.
      GtkDropTargetAsync retains the full old functionality and allows
      handling mime types.
    
    - Drop events are handled differently
      Instead of picking a single drop target and sending all DND events to
      it, every event is sent to every drop target. The first one to handle
      the event gets to call gdk_drop_status(), further handlers do not
      interact with the GdkDrop.
      Of course, for the ultimate GDK_DROP_STARTING event, only the first
      one to accept the drop gets to handle it.
      This allows stacking DND event controllers that aren't necessarily
      interested in handling the event or that might decide later to drop
      it.
    
    - Port all widgets to either of those
      Both have a somewhat changed API due to the new event handling.
      For the ones who should use the sync version, lots of cleanup was
      involved to operate on a sync API.

 demos/gtk-demo/clipboard.c           |  48 +-
 docs/reference/gtk/gtk4-sections.txt |  31 +-
 gtk/gtk.h                            |   3 +-
 gtk/gtkcalendar.c                    | 212 ++------
 gtk/gtkcolorbutton.c                 |  43 +-
 gtk/gtkcolorswatch.c                 |  36 +-
 gtk/gtkdragdest.c                    | 871 ------------------------------
 gtk/gtkdragdest.h                    |  83 ---
 gtk/gtkdrop.c                        | 113 ++++
 gtk/gtkdropprivate.h                 |  38 ++
 gtk/gtkdroptarget.c                  | 990 +++++++++++++++++++++++++++++++++++
 gtk/gtkdroptarget.h                  |  86 +++
 gtk/gtkdroptargetasync.c             | 673 ++++++++++++++++++++++++
 gtk/gtkdroptargetasync.h             |  77 +++
 gtk/gtkfilechooserbutton.c           | 292 ++++-------
 gtk/gtkfilechooserwidget.c           |  56 +-
 gtk/gtkiconview.c                    |  91 ++--
 gtk/gtkiconviewprivate.h             |   6 +-
 gtk/gtklistbox.c                     |   1 -
 gtk/gtkmain.c                        |  35 +-
 gtk/gtknotebook.c                    | 160 +++---
 gtk/gtkplacessidebar.c               | 296 ++++-------
 gtk/gtktext.c                        | 183 +++----
 gtk/gtktextview.c                    | 129 ++---
 gtk/gtktreeview.c                    | 100 ++--
 gtk/gtkwidget.c                      |   2 +-
 gtk/gtkwindow.c                      |  32 +-
 gtk/meson.build                      |   7 +-
 tests/testdnd.c                      | 214 +++-----
 tests/testdnd2.c                     |  26 +-
 tests/testdnd3.c                     |  88 +---
 tests/testlist3.c                    |  39 +-
 tests/testnotebookdnd.c              |  50 +-
 tests/testtreednd.c                  |  24 +-
 testsuite/gtk/defaultvalue.c         |   3 +-
 35 files changed, 2742 insertions(+), 2396 deletions(-)
---
diff --git a/demos/gtk-demo/clipboard.c b/demos/gtk-demo/clipboard.c
index 9187c94641..1871e4a506 100644
--- a/demos/gtk-demo/clipboard.c
+++ b/demos/gtk-demo/clipboard.c
@@ -152,43 +152,17 @@ prepare_drag (GtkDragSource *source,
   return gdk_content_provider_new_typed (GDK_TYPE_TEXTURE, paintable);
 }
 
-static void
-got_texture (GObject *source,
-             GAsyncResult *result,
-             gpointer data)
-{
-  GdkDrop *drop = GDK_DROP (source);
-  GtkWidget *image = data;
-  const GValue *value;
-  GError *error = NULL;
-
-  value = gdk_drop_read_value_finish (drop, result, &error);
-  if (value)
-    {
-      GdkTexture *texture = g_value_get_object (value);
-      gtk_image_set_from_paintable (GTK_IMAGE (image), GDK_PAINTABLE (texture));
-    }
-  else
-    {
-      g_print ("Failed to get data: %s\n", error->message);
-      g_error_free (error);
-    }
-}
-
 static gboolean
 drag_drop (GtkDropTarget *dest,
-           GdkDrop       *drop,
-           int            x,
-           int            y,
-           GtkWidget     *widget)
+           const GValue  *value,
+           double         x,
+           double         y,
+           GtkImage      *image)
 {
-  if (gdk_drop_has_value (drop, GDK_TYPE_TEXTURE))
-    {
-      gdk_drop_read_value_async (drop, GDK_TYPE_TEXTURE, G_PRIORITY_DEFAULT, NULL, got_texture, widget);
-      return TRUE;
-    }
+  GdkTexture *texture = g_value_get_object (value);
+  gtk_image_set_from_paintable (GTK_IMAGE (image), GDK_PAINTABLE (texture));
 
-  return FALSE;
+  return TRUE;
 }
 
 static void
@@ -354,8 +328,8 @@ do_clipboard (GtkWidget *do_widget)
       gtk_widget_add_controller (image, GTK_EVENT_CONTROLLER (source));
 
       /* accept drops on image */
-      dest = gtk_drop_target_new (gdk_content_formats_new_for_gtype (GDK_TYPE_TEXTURE), GDK_ACTION_COPY);
-      g_signal_connect (dest, "drag-drop", G_CALLBACK (drag_drop), image);
+      dest = gtk_drop_target_new (GDK_TYPE_TEXTURE, GDK_ACTION_COPY);
+      g_signal_connect (dest, "drop", G_CALLBACK (drag_drop), image);
       gtk_widget_add_controller (image, GTK_EVENT_CONTROLLER (dest));
 
       /* context menu on image */
@@ -383,8 +357,8 @@ do_clipboard (GtkWidget *do_widget)
       gtk_widget_add_controller (image, GTK_EVENT_CONTROLLER (source));
 
       /* accept drops on image */
-      dest = gtk_drop_target_new (gdk_content_formats_new_for_gtype (GDK_TYPE_TEXTURE), GDK_ACTION_COPY);
-      g_signal_connect (dest, "drag-drop", G_CALLBACK (drag_drop), image);
+      dest = gtk_drop_target_new (GDK_TYPE_TEXTURE, GDK_ACTION_COPY);
+      g_signal_connect (dest, "drop", G_CALLBACK (drag_drop), image);
       gtk_widget_add_controller (image, GTK_EVENT_CONTROLLER (dest));
 
       /* context menu on image */
diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt
index 008ab81f1e..08dc019e08 100644
--- a/docs/reference/gtk/gtk4-sections.txt
+++ b/docs/reference/gtk/gtk4-sections.txt
@@ -6813,14 +6813,16 @@ gtk_drag_source_get_type
 <FILE>gtkdroptarget</FILE>
 GtkDropTarget
 gtk_drop_target_new
-gtk_drop_target_set_formats
+gtk_drop_target_set_gtypes
+gtk_drop_target_get_gtypes
 gtk_drop_target_get_formats
 gtk_drop_target_set_actions
 gtk_drop_target_get_actions
+gtk_drop_target_set_preload
+gtk_drop_target_get_preload
 gtk_drop_target_get_drop
-gtk_drop_target_find_mimetype
-gtk_drag_highlight
-gtk_drag_unhighlight
+gtk_drop_target_get_value
+gtk_drop_target_reject
 
 <SUBSECTION Standard>
 GTK_TYPE_DROP_TARGET
@@ -6833,6 +6835,27 @@ GTK_DROP_TARGET_GET_CLASS
 gtk_drop_target_get_type
 </SECTION>
 
+<SECTION>
+<FILE>gtkdroptargetasync</FILE>
+GtkDropTargetAsync
+gtk_drop_target_async_new
+gtk_drop_target_async_set_formats
+gtk_drop_target_async_get_formats
+gtk_drop_target_async_set_actions
+gtk_drop_target_async_get_actions
+gtk_drop_target_async_reject_drop
+
+<SUBSECTION Standard>
+GTK_TYPE_DROP_TARGET_ASYNC
+GTK_DROP_TARGET_ASYNC
+GTK_DROP_TARGET_ASYNC_CLASS
+GTK_IS_DROP_TARGET_ASYNC
+GTK_IS_DROP_TARGET_ASYNC_CLASS
+GTK_DROP_TARGET_ASYNC_GET_CLASS
+<SUBSECTION Private>
+gtk_drop_target_async_get_type
+</SECTION>
+
 <SECTION>
 <FILE>gtkdropcontrollermotion</FILE>
 <TITLE>GtkDropControllerMotion</TITLE>
diff --git a/gtk/gtk.h b/gtk/gtk.h
index 8ee2ba1b0b..603fe9f9fd 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -90,11 +90,12 @@
 #include <gtk/gtkcustomlayout.h>
 #include <gtk/gtkdebug.h>
 #include <gtk/gtkdialog.h>
-#include <gtk/gtkdragdest.h>
 #include <gtk/gtkdragicon.h>
 #include <gtk/gtkdragsource.h>
 #include <gtk/gtkdrawingarea.h>
 #include <gtk/gtkdropcontrollermotion.h>
+#include <gtk/gtkdroptarget.h>
+#include <gtk/gtkdroptargetasync.h>
 #include <gtk/gtkeditable.h>
 #include <gtk/gtkemojichooser.h>
 #include <gtk/gtkentry.h>
diff --git a/gtk/gtkcalendar.c b/gtk/gtkcalendar.c
index eb63b38db0..ec71a373cc 100644
--- a/gtk/gtkcalendar.c
+++ b/gtk/gtkcalendar.c
@@ -95,7 +95,7 @@
 #endif
 
 #include "gtkcalendar.h"
-#include "gtkdragdest.h"
+#include "gtkdroptarget.h"
 #include "gtkintl.h"
 #include "gtkmain.h"
 #include "gtkmarshalers.h"
@@ -285,17 +285,6 @@ static void     gtk_calendar_focus_controller_focus     (GtkEventController    *
                                                          GtkWidget             *widget);
 static void     gtk_calendar_state_flags_changed  (GtkWidget     *widget,
                                                    GtkStateFlags  previous_state);
-static gboolean gtk_calendar_drag_accept        (GtkDropTarget    *dest,
-                                                 GdkDrop          *drop,
-                                                 GtkCalendar      *calendar);
-static void     gtk_calendar_drag_leave         (GtkDropTarget    *dest,
-                                                 GdkDrop          *drop,
-                                                 GtkCalendar      *calendar);
-static gboolean gtk_calendar_drag_drop          (GtkDropTarget    *dest,
-                                                 GdkDrop          *drop,
-                                                 int               x,
-                                                 int               y,
-                                                 GtkCalendar      *calendar);
 
 
 static void calendar_invalidate_day     (GtkCalendar *widget,
@@ -320,6 +309,57 @@ static char    *default_monthname[12];
 
 G_DEFINE_TYPE_WITH_PRIVATE (GtkCalendar, gtk_calendar, GTK_TYPE_WIDGET)
 
+static void
+gtk_calendar_drag_notify_value (GtkDropTarget  *target,
+                                GParamSpec    **pspec,
+                                GtkCalendar    *calendar)
+{
+  GDate *date;
+  const GValue *value;
+
+  value = gtk_drop_target_get_value (target);
+  if (value == NULL)
+    return;
+
+  date = g_date_new ();
+  g_date_set_parse (date, g_value_get_string (value));
+  if (!g_date_valid (date))
+    gtk_drop_target_reject (target);
+  g_date_free (date);
+}
+
+static gboolean
+gtk_calendar_drag_drop (GtkDropTarget  *dest,
+                        const GValue   *value,
+                        double          x,
+                        double          y,
+                        GtkCalendar    *calendar)
+{
+  GDate *date;
+  GDateTime *datetime;
+
+  date = g_date_new ();
+  g_date_set_parse (date, g_value_get_string (value));
+
+  if (!g_date_valid (date))
+    {
+      g_warning ("Received invalid date data");
+      g_date_free (date);
+      return FALSE;
+    }
+
+  datetime = g_date_time_new_local (g_date_get_year (date),
+                                    g_date_get_month (date),
+                                    g_date_get_day (date),
+                                    0, 0, 0);
+  g_date_free (date);
+
+  gtk_calendar_select_day (calendar, datetime);
+  g_date_time_unref (datetime);
+
+  return TRUE;
+}
+
 static void
 gtk_calendar_dispose (GObject *object)
 {
@@ -518,6 +558,7 @@ gtk_calendar_init (GtkCalendar *calendar)
   GtkWidget *widget = GTK_WIDGET (calendar);
   GtkEventController *controller;
   GtkGesture *gesture;
+  GtkDropTarget *target;
   gint i;
 #ifdef G_OS_WIN32
   wchar_t wbuffer[100];
@@ -535,7 +576,6 @@ gtk_calendar_init (GtkCalendar *calendar)
 #else
   gchar *week_start;
 #endif
-  GtkDropTarget *dest;
   int min_year_width;
   GDateTime *now;
 
@@ -718,14 +758,11 @@ gtk_calendar_init (GtkCalendar *calendar)
 
   priv->in_drag = 0;
 
-  dest = gtk_drop_target_new (gdk_content_formats_new_for_gtype (G_TYPE_STRING),
-                              GDK_ACTION_COPY);
-
-  g_signal_connect (dest, "accept", G_CALLBACK (gtk_calendar_drag_accept), calendar);
-  g_signal_connect (dest, "drag-leave", G_CALLBACK (gtk_calendar_drag_leave), calendar);
-  g_signal_connect (dest, "drag-drop", G_CALLBACK (gtk_calendar_drag_drop), calendar);
-
-  gtk_widget_add_controller (widget, GTK_EVENT_CONTROLLER (dest));
+  target = gtk_drop_target_new (G_TYPE_STRING, GDK_ACTION_COPY);
+  gtk_drop_target_set_preload (target, TRUE);
+  g_signal_connect (target, "notify::value", G_CALLBACK (gtk_calendar_drag_notify_value), calendar);
+  g_signal_connect (target, "drop", G_CALLBACK (gtk_calendar_drag_drop), calendar);
+  gtk_widget_add_controller (widget, GTK_EVENT_CONTROLLER (target));
 
   priv->year_before = 0;
 
@@ -1417,139 +1454,6 @@ gtk_calendar_state_flags_changed (GtkWidget     *widget,
     }
 }
 
-/* Get/set whether drag_motion requested the drag data and
- * drag_data_received should thus not actually insert the data,
- * since the data doesn’t result from a drop.
- */
-static void
-set_status_pending (GdkDrop       *drop,
-                    GdkDragAction  suggested_action)
-{
-  g_object_set_data (G_OBJECT (drop),
-                     I_("gtk-calendar-status-pending"),
-                     GINT_TO_POINTER (suggested_action));
-}
-
-static GdkDragAction
-get_status_pending (GdkDrop *drop)
-{
-  return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (drop),
-                                             "gtk-calendar-status-pending"));
-}
-
-static void
-gtk_calendar_drag_leave (GtkDropTarget *dest,
-                         GdkDrop       *drop,
-                         GtkCalendar   *calendar)
-{
-}
-
-static void
-got_text (GObject      *source,
-          GAsyncResult *result,
-          gpointer      data)
-{
-  GtkDropTarget *dest = GTK_DROP_TARGET (data);
-  GtkCalendar *calendar = GTK_CALENDAR (gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest)));
-  GdkDrop *drop = GDK_DROP (source);
-  gchar *str;
-  GDate *date;
-  GDateTime *datetime;
-  GdkDragAction suggested_action;
-
-  suggested_action = get_status_pending (drop);
-  set_status_pending (drop, 0);
-
-  str = gdk_drop_read_text_finish (drop, result, NULL);
-
-  if (suggested_action)
-    {
-      if (str)
-        {
-          date = g_date_new ();
-          g_date_set_parse (date, str);
-          if (!g_date_valid (date))
-              suggested_action = 0;
-          g_date_free (date);
-          g_free (str);
-        }
-      else
-        suggested_action = 0;
-      gdk_drop_status (drop, suggested_action);
-      if (suggested_action == 0)
-        gtk_drop_target_deny_drop (dest, drop);
-      return;
-    }
-
-  date = g_date_new ();
-  if (str)
-    {
-      g_date_set_parse (date, str);
-      g_free (str);
-    }
-
-  if (!g_date_valid (date))
-    {
-      g_warning ("Received invalid date data");
-      g_date_free (date);
-      gdk_drop_finish (drop, 0);
-      gtk_drop_target_deny_drop (dest, drop);
-      return;
-    }
-
-  datetime = g_date_time_new_local (g_date_get_year (date),
-                                    g_date_get_month (date),
-                                    g_date_get_day (date),
-                                    0, 0, 0);
-  g_date_free (date);
-
-  gdk_drop_finish (drop, suggested_action);
-
-  gtk_calendar_select_day (calendar, datetime);
-  g_date_time_unref (datetime);
-}
-
-static gboolean
-gtk_calendar_drag_accept (GtkDropTarget *dest,
-                          GdkDrop       *drop,
-                          GtkCalendar   *calendar)
-{
-  const char *target;
-
-  target = gtk_drop_target_find_mimetype (dest);
-  if (!target || gdk_drop_get_actions (drop) == 0)
-    {
-      gdk_drop_status (drop, 0);
-      return FALSE;
-    }
-  else if (get_status_pending (drop) == 0)
-    {
-      set_status_pending (drop, gdk_drop_get_actions (drop));
-      gdk_drop_read_text_async (drop, NULL, got_text, dest);
-    }
-  return TRUE;
-}
-
-static gboolean
-gtk_calendar_drag_drop (GtkDropTarget  *dest,
-                        GdkDrop        *drop,
-                        int             x,
-                        int             y,
-                        GtkCalendar    *calendar)
-{
-  const char *target;
-
-  target = gtk_drop_target_find_mimetype (dest);
-  if (target != NULL)
-    {
-      set_status_pending (drop, 0);
-      gdk_drop_read_text_async (drop, NULL, got_text, dest);
-      return TRUE;
-    }
-
-  return FALSE;
-}
-
 
 /****************************************
  *              Public API              *
diff --git a/gtk/gtkcolorbutton.c b/gtk/gtkcolorbutton.c
index 9c951d21ae..17f52fba7d 100644
--- a/gtk/gtkcolorbutton.c
+++ b/gtk/gtkcolorbutton.c
@@ -36,17 +36,14 @@
 #include "gtkcolorchooserprivate.h"
 #include "gtkcolorchooserdialog.h"
 #include "gtkcolorswatchprivate.h"
-#include "gtkdragdest.h"
 #include "gtkdragsource.h"
+#include "gtkdroptarget.h"
 #include "gtkintl.h"
 #include "gtkmain.h"
 #include "gtkmarshalers.h"
 #include "gtkprivate.h"
 #include "gtksnapshot.h"
 #include "gtkstylecontext.h"
-#include "gtkdragsource.h"
-#include "gtkdragdest.h"
-#include "gtkeventcontroller.h"
 
 
 /**
@@ -230,33 +227,16 @@ gtk_color_button_class_init (GtkColorButtonClass *klass)
   gtk_widget_class_set_css_name (widget_class, "colorbutton");
 }
 
-static void
-got_color (GObject      *source,
-           GAsyncResult *result,
-           gpointer      data)
-{
-  GdkDrop *drop = GDK_DROP (source);
-  const GValue *value;
-
-  value = gdk_drop_read_value_finish (drop, result, NULL);
-  if (value)
-    {
-      GdkRGBA *color = g_value_get_boxed (value);
-      gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (data), color);
-      gdk_drop_finish (drop, GDK_ACTION_COPY);
-    }
-  else
-    gdk_drop_finish (drop, 0);
-}
-
 static gboolean
-gtk_color_button_drag_drop (GtkDropTarget  *dest,
-                            GdkDrop        *drop,
-                            int             x,
-                            int             y,
-                            GtkColorButton *button)
+gtk_color_button_drop (GtkDropTarget  *dest,
+                       const GValue   *value,
+                       double          x,
+                       double          y,
+                       GtkColorButton *button)
 {
-  gdk_drop_read_value_async (drop, GDK_TYPE_RGBA, G_PRIORITY_DEFAULT, NULL, got_color, button);
+  GdkRGBA *color = g_value_get_boxed (value);
+
+  gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (button), color);
   return TRUE;
 }
 
@@ -320,9 +300,8 @@ gtk_color_button_init (GtkColorButton *button)
   priv->rgba.alpha = 1;
   priv->use_alpha = FALSE;
 
-  dest = gtk_drop_target_new (gdk_content_formats_new_for_gtype (GDK_TYPE_RGBA),
-                              GDK_ACTION_COPY);
-  g_signal_connect (dest, "drag-drop", G_CALLBACK (gtk_color_button_drag_drop), button);
+  dest = gtk_drop_target_new (GDK_TYPE_RGBA, GDK_ACTION_COPY);
+  g_signal_connect (dest, "drop", G_CALLBACK (gtk_color_button_drop), button);
   gtk_widget_add_controller (GTK_WIDGET (button), GTK_EVENT_CONTROLLER (dest));
 
   source = gtk_drag_source_new ();
diff --git a/gtk/gtkcolorswatch.c b/gtk/gtkcolorswatch.c
index f073a7ff68..ae424ad572 100644
--- a/gtk/gtkcolorswatch.c
+++ b/gtk/gtkcolorswatch.c
@@ -22,8 +22,8 @@
 #include "gtkbox.h"
 #include "gtkcolorchooserprivate.h"
 #include "gtkcssnodeprivate.h"
-#include "gtkdragdest.h"
 #include "gtkdragsource.h"
+#include "gtkdroptarget.h"
 #include "gtkgesturelongpress.h"
 #include "gtkgestureclick.h"
 #include "gtkgesturesingle.h"
@@ -133,33 +133,16 @@ gtk_color_swatch_drag_begin (GtkDragSource  *source,
   g_object_unref (paintable);
 }
 
-static void
-got_color (GObject      *source,
-           GAsyncResult *result,
-           gpointer      data)
-{
-  GdkDrop *drop = GDK_DROP (source);
-  const GValue *value;
-
-  value = gdk_drop_read_value_finish (drop, result, NULL);
-  if (value)
-    {
-      GdkRGBA *color = g_value_get_boxed (value);
-      gtk_color_swatch_set_rgba (GTK_COLOR_SWATCH (data), color);
-      gdk_drop_finish (drop, GDK_ACTION_COPY);
-    }
-  else
-    gdk_drop_finish (drop, 0);
-}
-
 static gboolean
 swatch_drag_drop (GtkDropTarget  *dest,
-                  GdkDrop        *drop,
-                  int             x,
-                  int             y,
+                  const GValue   *value,
+                  double          x,
+                  double          y,
                   GtkColorSwatch *swatch)
+
 {
-  gdk_drop_read_value_async (drop, GDK_TYPE_RGBA, G_PRIORITY_DEFAULT, NULL, got_color, swatch);
+  gtk_color_swatch_set_rgba (swatch, g_value_get_boxed (value));
+
   return TRUE;
 }
 
@@ -670,9 +653,8 @@ gtk_color_swatch_set_can_drop (GtkColorSwatch *swatch,
 
   if (can_drop && !priv->dest)
     {
-      priv->dest = gtk_drop_target_new (gdk_content_formats_new_for_gtype (GDK_TYPE_RGBA),
-                                        GDK_ACTION_COPY);
-      g_signal_connect (priv->dest, "drag-drop", G_CALLBACK (swatch_drag_drop), swatch);
+      priv->dest = gtk_drop_target_new (GDK_TYPE_RGBA, GDK_ACTION_COPY);
+      g_signal_connect (priv->dest, "drop", G_CALLBACK (swatch_drag_drop), swatch);
       gtk_widget_add_controller (GTK_WIDGET (swatch), GTK_EVENT_CONTROLLER (priv->dest));
     }
   if (!can_drop && priv->dest)
diff --git a/gtk/gtkdrop.c b/gtk/gtkdrop.c
new file mode 100644
index 0000000000..7c4c834dbc
--- /dev/null
+++ b/gtk/gtkdrop.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright © 2020 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 "config.h"
+
+#include "gtkdropprivate.h"
+
+typedef struct _GtkDrop GtkDrop;
+
+struct _GtkDrop
+{
+  /* TRUE if we are waiting for a gdk_drop_status() call */
+  gboolean waiting;
+  /* TRUE if begin_event() has been called but end_event() hasn't yet - purely for debugging */
+  gboolean active;
+};
+
+static void
+gtk_drop_free (gpointer data)
+{
+  GtkDrop *self = data;
+
+  g_slice_free (GtkDrop, self);
+}
+
+static GtkDrop *
+gtk_drop_lookup (GdkDrop *drop)
+{
+  static GQuark drop_quark = 0;
+  GtkDrop *result;
+
+  if (G_UNLIKELY (drop_quark == 0))
+    drop_quark = g_quark_from_string ("-gtk-drop-data");
+
+  result = g_object_get_qdata (G_OBJECT (drop), drop_quark);
+  if (result == NULL)
+    {
+      result = g_slice_new0 (GtkDrop);
+      g_object_set_qdata_full (G_OBJECT (drop), drop_quark, result, gtk_drop_free);
+    }
+
+  return result;
+}
+
+void
+gtk_drop_begin_event (GdkDrop      *drop,
+                      GdkEventType  event_type)
+{
+  GtkDrop *self;
+
+  self = gtk_drop_lookup (drop);
+
+  g_assert (self->waiting == FALSE);
+  g_assert (self->active == FALSE);
+
+  self->active = TRUE;
+  if (event_type == GDK_DRAG_ENTER ||
+      event_type == GDK_DRAG_MOTION)
+    self->waiting = TRUE;
+}
+
+void
+gtk_drop_end_event (GdkDrop *drop)
+{
+  GtkDrop *self;
+
+  self = gtk_drop_lookup (drop);
+
+  g_assert (self->active == TRUE);
+
+  if (self->waiting)
+    {
+      gdk_drop_status (drop, 0);
+      self->waiting = FALSE;
+    }
+  self->active = FALSE;
+}
+
+gboolean
+gtk_drop_status (GdkDrop       *drop,
+                 GdkDragAction  actions,
+                 GdkDragAction  preferred_action)
+{
+  GtkDrop *self;
+
+  self = gtk_drop_lookup (drop);
+
+  g_assert (self->active == TRUE);
+
+  if (!self->waiting)
+    return FALSE;
+
+  gdk_drop_status (drop, actions);
+  self->waiting = FALSE;
+  return TRUE;
+}
+                     
diff --git a/gtk/gtkdropprivate.h b/gtk/gtkdropprivate.h
new file mode 100644
index 0000000000..769013bfcd
--- /dev/null
+++ b/gtk/gtkdropprivate.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright © 2020 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>
+ */
+
+#ifndef __GTK_DROP_PRIVATE_H__
+#define __GTK_DROP_PRIVATE_H__
+
+#include <gdk/gdk.h>
+
+G_BEGIN_DECLS
+
+
+void                    gtk_drop_begin_event                    (GdkDrop                *drop,
+                                                                 GdkEventType            event_type);
+void                    gtk_drop_end_event                      (GdkDrop                *drop);
+
+gboolean                gtk_drop_status                         (GdkDrop                *drop,
+                                                                 GdkDragAction           actions,
+                                                                 GdkDragAction           preferred_action);
+
+G_END_DECLS
+
+#endif
diff --git a/gtk/gtkdroptarget.c b/gtk/gtkdroptarget.c
new file mode 100644
index 0000000000..3bd86fa93a
--- /dev/null
+++ b/gtk/gtkdroptarget.c
@@ -0,0 +1,990 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 1995-1999 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * 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/>.
+ */
+
+/*
+ * 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/.
+ */
+
+#include "config.h"
+
+#include "gtkdroptarget.h"
+
+#include "gtkdropprivate.h"
+#include "gtkeventcontrollerprivate.h"
+#include "gtkintl.h"
+#include "gtkmarshalers.h"
+#include "gtknative.h"
+#include "gtkprivate.h"
+#include "gtktypebuiltins.h"
+
+
+/**
+ * SECTION:gtkdroptarget
+ * @Short_description: Event controller to receive DND drops
+ * @Title: GtkDropTarget
+ * @See_also: #GdkDrop, #GtkDropTargetAsync
+ *
+ * GtkDropTarget is an event controller implementing a simple way to
+ * receive Drag-and-Drop operations.
+ *
+ * The most basic way to use a #GtkDropTarget to receive drops on a
+ * widget, is to create it via gtk_drop_target_new(), passing in the
+ * #GType of the data you want to receive and connect to the
+ * GtkDropTarget::drop signal to receive the data.
+ *
+ * #GtkDropTarget supports more options, such as:
+ *
+ *  * rejecting potential drops via the GtkDropTarget::accept signal
+ *    and the gtk_drop_target_reject() function to let other drop
+ *    targets handle the drop
+ *  * tracking an ongoing drag operation before the drop via the
+ *    GtkDropTarget::enter, GtkDropTarget::motion and
+ *    GtkDropTarget::leave signals
+ *  * configuring how to receive data by setting the
+ *    GtkDropTarget:preload property and listening for its availability
+ *    via the GtkDropTarget:value property
+ *
+ * However, #GtkDropTarget is ultimately modeled in a synchronous way
+ * and only supports data transferred via #GType.  
+ * If you want full control over an ongoing drop, the #GdkDropTargetAsync
+ * object gives you this ability.
+ *
+ * While a pointer is dragged over the drop target's widget and the drop
+ * has not been rejected, that widget will receive the
+ * %GTK_STATE_FLAG_DROP_ACTIVE state, which can be used to style the widget.
+ */
+
+struct _GtkDropTarget
+{
+  GtkEventController parent_object;
+
+  GdkContentFormats *formats;
+  GdkDragAction actions;
+  guint preload : 1;
+
+  guint dropping : 1;
+  graphene_point_t coords;
+  GdkDrop *drop;
+  GCancellable *cancellable; /* NULL unless doing a read of value */
+  GValue value;
+};
+
+struct _GtkDropTargetClass
+{
+  GtkEventControllerClass parent_class;
+
+  gboolean              (* accept)                              (GtkDropTarget  *self,
+                                                                 GdkDrop        *drop);
+  GdkDragAction         (* enter)                               (GtkDropTarget  *self,
+                                                                 double          x,
+                                                                 double          y);
+  GdkDragAction         (* motion)                              (GtkDropTarget  *self,
+                                                                 double          x,
+                                                                 double          y);
+  void                  (* leave)                               (GtkDropTarget  *self,
+                                                                 GdkDrop        *drop);
+  gboolean              (* drop)                                (GtkDropTarget  *self,
+                                                                 const GValue   *value,
+                                                                 double          x,
+                                                                 double          y);
+};
+
+enum {
+  PROP_0,
+  PROP_ACTIONS,
+  PROP_DROP,
+  PROP_FORMATS,
+  PROP_PRELOAD,
+  PROP_VALUE,
+  NUM_PROPERTIES
+};
+
+static GParamSpec *properties[NUM_PROPERTIES];
+
+enum {
+  ACCEPT,
+  ENTER,
+  MOTION,
+  LEAVE,
+  DROP,
+  NUM_SIGNALS
+};
+
+static guint signals[NUM_SIGNALS];
+
+G_DEFINE_TYPE (GtkDropTarget, gtk_drop_target, GTK_TYPE_EVENT_CONTROLLER);
+
+static void
+gtk_drop_target_end_drop (GtkDropTarget *self)
+{
+  if (self->drop == NULL)
+    return;
+
+  g_object_freeze_notify (G_OBJECT (self));
+
+  if (self->dropping)
+    {
+      gdk_drop_finish (self->drop, 0);
+      self->dropping = FALSE;
+    }
+
+  g_clear_object (&self->drop);
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DROP]);
+
+  if (G_IS_VALUE (&self->value))
+    {
+      g_value_unset (&self->value);
+      g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_VALUE]);
+    }
+
+  if (self->cancellable)
+    {
+      g_cancellable_cancel (self->cancellable);
+      g_clear_object (&self->cancellable);
+    }
+
+  gtk_widget_unset_state_flags (gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (self)),
+                                GTK_STATE_FLAG_DROP_ACTIVE);
+
+  g_object_thaw_notify (G_OBJECT (self));
+}
+
+static void
+gtk_drop_target_do_drop (GtkDropTarget *self)
+{
+  gboolean success;
+
+  g_assert (self->dropping);
+  g_assert (G_IS_VALUE (&self->value));
+
+  g_signal_emit (self, signals[DROP], 0, &self->value, self->coords.x, self->coords.y, &success);
+
+  if (success)
+    gdk_drop_finish (self->drop, gdk_drop_get_actions (self->drop));
+  else
+    gdk_drop_finish (self->drop, 0);
+
+  self->dropping = FALSE;
+
+  gtk_drop_target_end_drop (self);
+}
+
+static void
+gtk_drop_target_load_done (GObject      *source,
+                           GAsyncResult *res,
+                           gpointer      data)
+{
+  GtkDropTarget *self = data;
+  const GValue *value;
+  GError *error = NULL;
+
+  value = gdk_drop_read_value_finish (GDK_DROP (source), res, &error);
+  if (value == NULL)
+    {
+      /* If this happens, data/self is invalid */
+      if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+        {
+          g_clear_error (&error);
+          return;
+        }
+
+      g_clear_object (&self->cancellable);
+      /* XXX: Should this be a warning? */
+      g_warning ("Failed to receive drop data: %s", error->message);
+      g_clear_error (&error);
+      gtk_drop_target_end_drop (self);
+      return;
+    }
+
+  g_clear_object (&self->cancellable);
+  g_value_init (&self->value, G_VALUE_TYPE (value));
+  g_value_copy (value, &self->value);
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_VALUE]);
+
+  if (self->dropping)
+    gtk_drop_target_do_drop (self);
+}
+
+static gboolean
+gtk_drop_target_load (GtkDropTarget *self)
+{
+  g_assert (self->drop);
+
+  if (G_IS_VALUE (&self->value))
+    return TRUE;
+
+  if (self->cancellable)
+    return FALSE;
+
+  self->cancellable = g_cancellable_new ();
+
+  gdk_drop_read_value_async (self->drop, 
+                             gdk_content_formats_match_gtype (self->formats, gdk_drop_get_formats 
(self->drop)),
+                             G_PRIORITY_DEFAULT,
+                             self->cancellable,
+                             gtk_drop_target_load_done,
+                             g_object_ref (self));
+  return FALSE;
+}
+
+static void
+gtk_drop_target_start_drop (GtkDropTarget *self,
+                            GdkDrop       *drop)
+{
+  g_object_freeze_notify (G_OBJECT (self));
+
+  gtk_drop_target_end_drop (self);
+
+  self->drop = g_object_ref (drop);
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DROP]);
+
+  if (self->preload)
+    gtk_drop_target_load (self);
+
+  gtk_widget_set_state_flags (gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (self)),
+                              GTK_STATE_FLAG_DROP_ACTIVE,
+                              FALSE);
+
+  g_object_thaw_notify (G_OBJECT (self));
+}
+
+static gboolean
+gtk_drop_target_accept (GtkDropTarget *self,
+                        GdkDrop       *drop)
+{
+  if ((gdk_drop_get_actions (drop) & gtk_drop_target_get_actions (self)) == 0)
+    return FALSE;
+
+  if (self->formats == NULL)
+    return TRUE;
+
+  return gdk_content_formats_match (self->formats, gdk_drop_get_formats (drop));
+}
+
+static GdkDragAction
+make_action_unique (GdkDragAction actions)
+{
+  if (actions & GDK_ACTION_COPY)
+    return GDK_ACTION_COPY;
+
+  if (actions & GDK_ACTION_MOVE)
+    return GDK_ACTION_MOVE;
+  
+  if (actions & GDK_ACTION_LINK)
+    return GDK_ACTION_LINK;
+
+  return 0;
+}
+  
+static GdkDragAction
+gtk_drop_target_enter (GtkDropTarget  *self,
+                       double          x,
+                       double          y)
+{
+  return make_action_unique (self->actions & gdk_drop_get_actions (self->drop));
+}
+
+static GdkDragAction         
+gtk_drop_target_motion (GtkDropTarget  *self,
+                        double          x,
+                        double          y)
+{
+  return make_action_unique (self->actions & gdk_drop_get_actions (self->drop));
+}
+
+static gboolean
+gtk_drop_target_drop (GtkDropTarget  *self,
+                      const GValue   *value,
+                      double          x,
+                      double          y)
+{
+  return FALSE;
+}
+
+static gboolean
+gtk_drop_target_filter_event (GtkEventController *controller,
+                              GdkEvent           *event)
+{
+  switch ((int) gdk_event_get_event_type (event))
+    {
+    case GDK_DRAG_ENTER:
+    case GDK_DRAG_LEAVE:
+    case GDK_DRAG_MOTION:
+    case GDK_DROP_START:
+      return GTK_EVENT_CONTROLLER_CLASS (gtk_drop_target_parent_class)->filter_event (controller, event);
+
+    default:;
+    }
+
+  return TRUE;
+}
+
+static gboolean
+gtk_drop_target_handle_event (GtkEventController *controller,
+                              GdkEvent           *event,
+                              double              x,
+                              double              y)
+{
+  GtkDropTarget *self = GTK_DROP_TARGET (controller);
+
+  /* All drops have been rejected. New drops only arrive via crossing
+   * events, so we can: */
+  if (self->drop == NULL)
+    return FALSE;
+
+  switch ((int) gdk_event_get_event_type (event))
+    {
+    case GDK_DRAG_MOTION:
+      {
+        GtkWidget *widget = gtk_event_controller_get_widget (controller);
+        GdkDragAction preferred;
+
+        /* sanity check */
+        g_return_val_if_fail (self->drop == gdk_drag_event_get_drop (event), FALSE);
+
+        graphene_point_init (&self->coords, x, y);
+        g_signal_emit (self, signals[MOTION], 0, x, y, &preferred);
+        if (preferred &&
+            gtk_drop_status (self->drop, self->actions, preferred))
+          {
+            gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE, FALSE);
+          }
+        else
+          {
+            gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE);
+          }
+      }
+      return FALSE;
+
+    case GDK_DROP_START:
+      {
+        /* sanity check */
+        g_return_val_if_fail (self->drop == gdk_drag_event_get_drop (event), FALSE);
+
+        graphene_point_init (&self->coords, x, y);
+        self->dropping = TRUE;
+        if (gtk_drop_target_load (self))
+          gtk_drop_target_do_drop (self);
+
+        return TRUE;
+      }
+
+    default:
+      return FALSE;
+    }
+}
+
+static void
+gtk_drop_target_handle_crossing (GtkEventController    *controller,
+                                 const GtkCrossingData *crossing,
+                                 double                 x,
+                                 double                 y)
+{
+  GtkDropTarget *self = GTK_DROP_TARGET (controller);
+  GtkWidget *widget = gtk_event_controller_get_widget (controller);
+
+  if (crossing->type != GTK_CROSSING_DROP)
+    return;
+
+  /* sanity check */
+  g_warn_if_fail (self->drop == NULL || self->drop == crossing->drop);
+
+  if (crossing->direction == GTK_CROSSING_IN)
+    {
+      gboolean accept = FALSE;
+      GdkDragAction preferred;
+
+      if (self->drop != NULL)
+        return;
+
+      /* if we were a target already but self->drop == NULL, the drop
+       * was rejected already */
+      if (crossing->old_descendent != NULL ||
+          crossing->old_target == widget)
+        return;
+
+      g_signal_emit (self, signals[ACCEPT], 0, crossing->drop, &accept);
+      if (!accept)
+        return;
+
+      graphene_point_init (&self->coords, x, y);
+      gtk_drop_target_start_drop (self, crossing->drop);
+
+      g_signal_emit (self, signals[ENTER], 0, x, y, &preferred);
+      if (preferred &&
+          gtk_drop_status (self->drop, self->actions, preferred))
+        {
+          gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE, FALSE);
+        }
+      else
+        {
+          gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE);
+        }
+    }
+  else
+    {
+      if (crossing->new_descendent != NULL ||
+          crossing->new_target == widget)
+        return;
+
+      g_signal_emit (self, signals[LEAVE], 0, self->drop);
+      if (!self->dropping)
+        gtk_drop_target_end_drop (self);
+      gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE);
+    }
+}
+
+static void
+gtk_drop_target_finalize (GObject *object)
+{
+  GtkDropTarget *self = GTK_DROP_TARGET (object);
+
+  g_clear_pointer (&self->formats, gdk_content_formats_unref);
+
+  G_OBJECT_CLASS (gtk_drop_target_parent_class)->finalize (object);
+}
+
+static void
+gtk_drop_target_set_property (GObject      *object,
+                              guint         prop_id,
+                              const GValue *value,
+                              GParamSpec   *pspec)
+{
+  GtkDropTarget *self = GTK_DROP_TARGET (object);
+
+  switch (prop_id)
+    {
+    case PROP_ACTIONS:
+      gtk_drop_target_set_actions (self, g_value_get_flags (value));
+      break;
+
+    case PROP_PRELOAD:
+      gtk_drop_target_set_preload (self, g_value_get_boolean (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtk_drop_target_get_property (GObject    *object,
+                              guint       prop_id,
+                              GValue     *value,
+                              GParamSpec *pspec)
+{
+  GtkDropTarget *self = GTK_DROP_TARGET (object);
+
+  switch (prop_id)
+    {
+    case PROP_ACTIONS:
+      g_value_set_flags (value, self->actions);
+      break;
+
+    case PROP_DROP:
+      g_value_set_object (value, self->drop);
+      break;
+
+    case PROP_FORMATS:
+      g_value_set_boxed (value, self->formats);
+      break;
+
+    case PROP_PRELOAD:
+      g_value_set_boolean (value, self->preload);
+      break;
+
+    case PROP_VALUE:
+      if (G_IS_VALUE (&self->value))
+        g_value_set_boxed (value, &self->value);
+      else
+        g_value_set_boxed (value, NULL);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtk_drop_target_class_init (GtkDropTargetClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+  GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (class);
+
+  object_class->finalize = gtk_drop_target_finalize;
+  object_class->set_property = gtk_drop_target_set_property;
+  object_class->get_property = gtk_drop_target_get_property;
+
+  controller_class->handle_event = gtk_drop_target_handle_event;
+  controller_class->filter_event = gtk_drop_target_filter_event;
+  controller_class->handle_crossing = gtk_drop_target_handle_crossing;
+
+  class->accept = gtk_drop_target_accept;
+  class->enter = gtk_drop_target_enter;
+  class->motion = gtk_drop_target_motion;
+  class->drop = gtk_drop_target_drop;
+
+  /**
+   * GtkDropTarget:actions:
+   *
+   * The #GdkDragActions that this drop target supports
+   */ 
+  properties[PROP_ACTIONS] =
+       g_param_spec_flags ("actions",
+                           P_("Actions"),
+                           P_("The actions supported by this drop target"),
+                           GDK_TYPE_DRAG_ACTION, 0,
+                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
+
+  /**
+   * GtkDropTarget:drop:
+   *
+   * The #GdkDrop that is currently being performed
+   */
+  properties[PROP_DROP] =
+       g_param_spec_object ("drop",
+                            P_("Drop"),
+                            P_("Current drop"),
+                            GDK_TYPE_DROP,
+                            GTK_PARAM_READABLE);
+
+  /**
+   * GtkDropTarget:formats:
+   *
+   * The #GdkContentFormats that determine the supported data formats
+   */
+  properties[PROP_FORMATS] =
+       g_param_spec_boxed ("formats",
+                           P_("Formats"),
+                           P_("The supported formats"),
+                           GDK_TYPE_CONTENT_FORMATS,
+                           GTK_PARAM_READABLE);
+
+  /**
+   * GtkDropTarget:preload:
+   *
+   * Whether the drop data should be preloaded when the pointer is only
+   * hovering over the widget but has not been released.
+   *
+   * Setting this property allows finer grained reaction to an ongoing
+   * drop at the cost of loading more data.
+   *
+   * The default value for this property is %FALSE to avoid downloading
+   * huge amounts of data by accident.  
+   * For example, if somebody drags a full document of gigabytes of text
+   * from a text editor across a widget with a preloading drop target,
+   * this data will be downlaoded, even if the data is ultimately dropped
+   * elsewhere.
+   *
+   * For a lot of data formats, the amount of data is very small (like
+   * %GDK_TYPE_RGBA), so enabling this property does not hurt at all.
+   */
+  properties[PROP_PRELOAD] =
+       g_param_spec_boolean ("preload",
+                             P_("Preload"),
+                             P_("Whether drop data should be preloaded while hovering"),
+                             FALSE,
+                             G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
+
+  /**
+   * GtkDropTarget:value:
+   *
+   * The value for this drop operation or %NULL if the data has not been
+   * loaded yet or no drop operation is going on.
+   *
+   * Data may be available before the GtkDropTarget::drop signal gets emitted -
+   * for example when the GtkDropTarget:preload property is set.
+   * You can use the GObject::notify signal to be notified of available data.
+   */
+  properties[PROP_VALUE] =
+       g_param_spec_boxed ("value",
+                           P_("Value"),
+                           P_("The value for this drop operation"),
+                           G_TYPE_VALUE,
+                           GTK_PARAM_READABLE);
+
+  g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
+
+ /**
+   * GtkDropTarget::accept:
+   * @self: the #GtkDropTarget
+   * @drop: the #GdkDrop
+   *
+   * The ::accept signal is emitted on the drop site when a drop operation
+   * is about to begin.  
+   * If the drop is not accepted, %FALSE will be returned and the drop target
+   * will ignore the drop. If %TRUE is returned, the drop is accepted for now
+   * but may be rejected later via a call to gtk_drop_target_reject() or
+   * ultimately by returning %FALSE from GtkDropTarget::drop
+   *
+   * The default handler for this signal decides whether to accept the drop
+   * based on the formats provided by the @drop.
+   *
+   * If the decision whether the drop will be accepted or rejected needs
+   * inspecting the data, this function should return %TRUE, the 
+   * GtkDropTarget:preload property should be set and the value
+   * should be inspected via the GObject::notify:value signal and then call
+   * gtk_drop_target_reject().
+   *
+   * Returns: %TRUE if @drop is accepted
+   */
+  signals[ACCEPT] =
+      g_signal_new (I_("accept"),
+                    G_TYPE_FROM_CLASS (class),
+                    G_SIGNAL_RUN_LAST,
+                    G_STRUCT_OFFSET (GtkDropTargetClass, accept),
+                    g_signal_accumulator_first_wins, NULL,
+                    NULL,
+                    G_TYPE_BOOLEAN, 1,
+                    GDK_TYPE_DROP);
+
+  /**
+   * GtkDropTarget::enter:
+   * @self: the #GtkDropTarget
+   * @x: the x coordinate of the current pointer position
+   * @y: the y coordinate of the current pointer position
+   *
+   * The ::enter signal is emitted on the drop site when the pointer
+   * enters the widget. It can be used to set up custom highlighting.
+   *
+   * Returns: Preferred action for this drag operation or 0 if dropping is not
+   *     supported at the current @x,@y location.
+   */
+  signals[ENTER] =
+      g_signal_new (I_("enter"),
+                    G_TYPE_FROM_CLASS (class),
+                    G_SIGNAL_RUN_LAST,
+                    G_STRUCT_OFFSET (GtkDropTargetClass, enter),
+                    g_signal_accumulator_first_wins, NULL,
+                    NULL,
+                    GDK_TYPE_DRAG_ACTION, 2,
+                    G_TYPE_DOUBLE, G_TYPE_DOUBLE);
+
+  /**
+   * GtkDropTarget::motion:
+   * @self: the #GtkDropTarget
+   * @x: the x coordinate of the current pointer position
+   * @y: the y coordinate of the current pointer position
+   *
+   * The ::motion signal is emitted while the pointer is moving
+   * over the drop target.
+   *
+   * Returns: Preferred action for this drag operation or 0 if dropping is not
+   *     supported at the current @x,@y location.
+   */
+  signals[MOTION] =
+      g_signal_new (I_("motion"),
+                    G_TYPE_FROM_CLASS (class),
+                    G_SIGNAL_RUN_LAST,
+                    G_STRUCT_OFFSET (GtkDropTargetClass, motion),
+                    g_signal_accumulator_first_wins, NULL,
+                    NULL,
+                    GDK_TYPE_DRAG_ACTION, 2,
+                    G_TYPE_DOUBLE, G_TYPE_DOUBLE);
+
+  /**
+   * GtkDropTarget::leave:
+   * @self: the #GtkDropTarget
+   * @drop: the #GdkDrop
+   *
+   * The ::leave signal is emitted on the drop site when the pointer
+   * leaves the widget. Its main purpose it to undo things done in
+   * #GtkDropTarget::enter.
+   */
+  signals[LEAVE] =
+      g_signal_new (I_("leave"),
+                    G_TYPE_FROM_CLASS (class),
+                    G_SIGNAL_RUN_LAST,
+                    G_STRUCT_OFFSET (GtkDropTargetClass, leave),
+                    NULL, NULL,
+                    NULL,
+                    G_TYPE_NONE, 0);
+
+  /**
+   * GtkDropTarget::drop:
+   * @self: the #GtkDropTarget
+   * @value: the #GValue being dropped
+   * @x: the x coordinate of the current pointer position
+   * @y: the y coordinate of the current pointer position
+   *
+   * The ::drop signal is emitted on the drop site when the user drops
+   * the data onto the widget. The signal handler must determine whether
+   * the pointer position is in a drop zone or not. If it is not in a drop
+   * zone, it returns %FALSE and no further processing is necessary.
+   *
+   * Otherwise, the handler returns %TRUE. In this case, this handler will
+   * accept the drop. The handler is responsible for rading the given @value
+   * and performing the drop operation.
+   *
+   * Returns: whether the drop was accepted at the given pointer position
+   */
+  signals[DROP] =
+      g_signal_new (I_("drop"),
+                    G_TYPE_FROM_CLASS (class),
+                    G_SIGNAL_RUN_LAST,
+                    0,
+                    g_signal_accumulator_first_wins, NULL,
+                    NULL,
+                    G_TYPE_BOOLEAN, 3,
+                    G_TYPE_VALUE, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
+}
+
+static void
+gtk_drop_target_init (GtkDropTarget *self)
+{
+  self->formats = gdk_content_formats_new (NULL, 0);
+}
+
+/**
+ * gtk_drop_target_new:
+ * @type: The supported type or %G_TYPE_INVALID
+ * @actions: the supported actions
+ *
+ * Creates a new #GtkDropTarget object.
+ *
+ * If the drop target should support more than 1 type, pass
+ * %G_TYPE_INVALID for @type and then call
+ * gtk_drop_target_set_gtypes().
+ *
+ * Returns: the new #GtkDropTarget
+ */
+GtkDropTarget *
+gtk_drop_target_new (GType         type,
+                     GdkDragAction actions)
+{
+  GtkDropTarget *result;
+
+  result = g_object_new (GTK_TYPE_DROP_TARGET,
+                         "actions", actions,
+                         NULL);
+
+  if (type != G_TYPE_INVALID)
+    gtk_drop_target_set_gtypes (result, &type, 1);
+
+  return result;
+}
+
+/**
+ * gtk_drop_target_get_formats:
+ * @self: a #GtkDropTarget
+ *
+ * Gets the data formats that this drop target accepts.
+ *
+ * If the result is %NULL, all formats are expected to be supported.
+ *
+ * Returns: (nullable): the supported data formats
+ */
+GdkContentFormats *
+gtk_drop_target_get_formats (GtkDropTarget *self)
+{
+  g_return_val_if_fail (GTK_IS_DROP_TARGET (self), NULL);
+  
+  return self->formats;
+}
+
+/**
+ * gtk_drop_target_set_gtypes:
+ * @self: a #GtkDropTarget
+ * @types: (nullable) (transfer none) (array length=n_types):
+ *     all supported #GTypes that can be dropped
+ * @n_types: number of @types
+ *
+ * Sets the supported #GTypes for this drop target.
+ *
+ * The GtkDropTarget::drop signal will 
+ **/
+void
+gtk_drop_target_set_gtypes (GtkDropTarget *self,
+                            GType         *types,
+                            gsize          n_types)
+{
+  GdkContentFormatsBuilder *builder;
+  gsize i;
+
+  g_return_if_fail (GTK_IS_DROP_TARGET (self));
+  g_return_if_fail (n_types == 0 || types != NULL);
+
+  gdk_content_formats_unref (self->formats);
+
+  builder = gdk_content_formats_builder_new ();
+  for (i = 0; i < n_types; i++)
+    gdk_content_formats_builder_add_gtype (builder, types[i]);
+  
+  self->formats = gdk_content_formats_builder_free_to_formats (builder);
+
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FORMATS]);
+}
+
+/**
+ * gtk_drop_target_get_gtypes:
+ * @self: a #GtkDropTarget
+ * @n_gtypes: (out) (allow-none): optional pointer to take the
+ *     number of #GTypes contained in the return value
+ *
+ * Gets the list of supported #GTypes for @self. If no type have been set,
+ * %NULL will be returned.
+ *
+ * Returns: (transfer none) (nullable): %G_TYPE_INVALID-terminated array of 
+ *     types included in @formats or %NULL if none.
+ **/
+const GType *
+gtk_drop_target_get_gtypes (GtkDropTarget *self,
+                            gsize         *n_types)
+{
+  g_return_val_if_fail (GTK_IS_DROP_TARGET (self), NULL);
+
+  return gdk_content_formats_get_gtypes (self->formats, n_types);
+}
+
+/**
+ * gtk_drop_target_set_actions:
+ * @self: a #GtkDropTarget
+ * @actions: the supported actions
+ *
+ * Sets the actions that this drop target supports.
+ */
+void
+gtk_drop_target_set_actions (GtkDropTarget *self,
+                             GdkDragAction  actions)
+{
+  g_return_if_fail (GTK_IS_DROP_TARGET (self));
+  
+  if (self->actions == actions)
+    return;
+
+  self->actions = actions;
+
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACTIONS]);
+}
+
+/**
+ * gtk_drop_target_get_actions:
+ * @self: a #GtkDropTarget
+ *
+ * Gets the actions that this drop target supports.
+ *
+ * Returns: the actions that this drop target supports
+ */
+GdkDragAction
+gtk_drop_target_get_actions (GtkDropTarget *self)
+{
+  g_return_val_if_fail (GTK_IS_DROP_TARGET (self), 0);
+
+  return self->actions;
+}
+
+/**
+ * gtk_drop_target_set_preload:
+ * @self: a #GtkDropTarget
+ * @preload: %TRUE to preload drop data
+ *
+ * Sets the GtkDropTarget:preload property.
+ **/
+void
+gtk_drop_target_set_preload (GtkDropTarget *self,
+                             gboolean       preload)
+{
+  g_return_if_fail (GTK_IS_DROP_TARGET (self));
+  
+  if (self->preload == preload)
+    return;
+
+  self->preload = preload;
+
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PRELOAD]);
+}
+
+/**
+ * gtk_drop_target_get_preload:
+ * @self: a #GtkDropTarget
+ *
+ * Gets the value of the GtkDropTarget:preload property.
+ *
+ * Returns: %TRUE if drop data should be preloaded
+ */
+gboolean
+gtk_drop_target_get_preload (GtkDropTarget *self)
+{
+  g_return_val_if_fail (GTK_IS_DROP_TARGET (self), 0);
+
+  return self->preload;
+}
+
+/**
+ * gtk_drop_target_get_drop:
+ * @self: a #GtkDropTarget
+ *
+ * Gets the currently handled drop operation.
+ *
+ * If no drop operation is going on, %NULL is returned.
+ *
+ * Returns: (nullable) (transfer none): The current drop
+ **/
+GdkDrop *
+gtk_drop_target_get_drop (GtkDropTarget *self)
+{
+  g_return_val_if_fail (GTK_IS_DROP_TARGET (self), NULL);
+
+  return self->drop;
+}
+
+/**
+ * gtk_drop_target_get_value:
+ * @self: a #GtkDropTarget
+ *
+ * Gets the value of the GtkDropTarget:value porperty.
+ *
+ * Returns: (nullable) (transfer none): The current drop data
+ **/
+const GValue *
+gtk_drop_target_get_value (GtkDropTarget *self)
+{
+  g_return_val_if_fail (GTK_IS_DROP_TARGET (self), NULL);
+
+  if (!G_IS_VALUE (&self->value))
+    return NULL;
+
+  return &self->value;
+}
+
+/**
+ * gtk_drop_target_reject:
+ * @self: a #GtkDropTarget
+ *
+ * Rejects the ongoing drop operation.
+ *
+ * If no drop operation is ongoing - when GdkDropTarget:drop
+ * returns %NULL - this function does nothing.
+ *
+ * This function should be used when delaying the decision
+ * on whether to accept a drag or not until after reading
+ * the data.
+ */
+void
+gtk_drop_target_reject (GtkDropTarget *self)
+{
+  g_return_if_fail (GTK_IS_DROP_TARGET (self));
+
+  if (self->drop == NULL)
+    return;
+
+  gtk_drop_target_end_drop (self);
+}
+
diff --git a/gtk/gtkdroptarget.h b/gtk/gtkdroptarget.h
new file mode 100644
index 0000000000..4020f15648
--- /dev/null
+++ b/gtk/gtkdroptarget.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright © 2020 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>
+ */
+
+#ifndef __GTK_DROP_TARGET_H__
+#define __GTK_DROP_TARGET_H__
+
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#include <gtk/gtktypes.h>
+
+
+G_BEGIN_DECLS
+
+typedef struct _GtkDropTarget GtkDropTarget;
+
+
+#define GTK_TYPE_DROP_TARGET         (gtk_drop_target_get_type ())
+#define GTK_DROP_TARGET(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_DROP_TARGET, GtkDropTarget))
+#define GTK_DROP_TARGET_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_DROP_TARGET, 
GtkDropTargetClass))
+#define GTK_IS_DROP_TARGET(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_DROP_TARGET))
+#define GTK_IS_DROP_TARGET_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_DROP_TARGET))
+#define GTK_DROP_TARGET_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_DROP_TARGET, 
GtkDropTargetClass))
+
+typedef struct _GtkDropTargetClass GtkDropTargetClass;
+
+GDK_AVAILABLE_IN_ALL
+GType                   gtk_drop_target_get_type         (void) G_GNUC_CONST;
+
+GDK_AVAILABLE_IN_ALL
+GtkDropTarget *         gtk_drop_target_new              (GType                  type,
+                                                          GdkDragAction          actions);
+
+GDK_AVAILABLE_IN_ALL
+void                    gtk_drop_target_set_gtypes       (GtkDropTarget         *self,
+                                                          GType                 *types,
+                                                          gsize                  n_types);
+GDK_AVAILABLE_IN_ALL
+const GType *           gtk_drop_target_get_gtypes       (GtkDropTarget         *self,
+                                                          gsize                 *n_types);
+GDK_AVAILABLE_IN_ALL
+GdkContentFormats *     gtk_drop_target_get_formats      (GtkDropTarget         *self);
+
+GDK_AVAILABLE_IN_ALL
+void                    gtk_drop_target_set_actions      (GtkDropTarget         *self,
+                                                          GdkDragAction          actions);
+GDK_AVAILABLE_IN_ALL
+GdkDragAction           gtk_drop_target_get_actions      (GtkDropTarget         *self);
+
+GDK_AVAILABLE_IN_ALL
+void                    gtk_drop_target_set_preload      (GtkDropTarget         *self,
+                                                          gboolean               preload);
+GDK_AVAILABLE_IN_ALL
+gboolean                gtk_drop_target_get_preload      (GtkDropTarget         *self);
+
+GDK_AVAILABLE_IN_ALL
+GdkDrop *               gtk_drop_target_get_drop         (GtkDropTarget         *self);
+
+GDK_AVAILABLE_IN_ALL
+const GValue *          gtk_drop_target_get_value        (GtkDropTarget         *self);
+
+GDK_AVAILABLE_IN_ALL
+void                    gtk_drop_target_reject           (GtkDropTarget         *self);
+
+
+G_END_DECLS
+
+#endif /* __GTK_DROP_TARGET_H__ */
diff --git a/gtk/gtkdroptargetasync.c b/gtk/gtkdroptargetasync.c
new file mode 100644
index 0000000000..0c2061927a
--- /dev/null
+++ b/gtk/gtkdroptargetasync.c
@@ -0,0 +1,673 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 1995-1999 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * 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/>.
+ */
+
+/*
+ * 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/.
+ */
+
+#include "config.h"
+
+#include "gtkdroptargetasync.h"
+
+#include "gtkdropprivate.h"
+#include "gtkeventcontrollerprivate.h"
+#include "gtkintl.h"
+#include "gtkmarshalers.h"
+#include "gtknative.h"
+#include "gtktypebuiltins.h"
+
+
+/**
+ * SECTION:gtkdroptargetasync
+ * @Short_description: Event controller to receive DND drops
+ * @Title: GtkDropTargetAsync
+ * @See_also: #GtkDropTarget
+ *
+ * GtkDropTargetAsync is an auxiliary object that can be used to receive
+ * Drag-and-Drop operations.  
+ * It is the more complete but also more complex method of handling drop
+ * operations compared to #GtkDropTarget and you should only use it if
+ * #GtkDropTarget doesn't provide all the features you need.
+ *
+ * To use a #GtkDropTargetAsync to receive drops on a widget, you create
+ * a #GtkDropTargetAsync object, configure which data formats and actions
+ * you support, connect to its signals, and then attach
+ * it to the widget with gtk_widget_add_controller().
+ *
+ * During a drag operation, the first signal that a GtkDropTargetAsync
+ * emits is #GtkDropTargetAsync::accept, which is meant to determine
+ * whether the target is a possible drop site for the ongoing drop.
+ * The default handler for the ::accept signal accepts the drop
+ * if it finds a compatible data format and an action that is supported
+ * on both sides.
+ *
+ * If it is, and the widget becomes a target, you will receive a
+ * #GtkDropTargetAsync::drag-enter signal, followed by
+ * #GtkDropTargetAsync::drag-motion signals as the pointer moves,
+ * optionally a #GtkDropTargetAsync::drop signal when a drop happens,
+ * and finally a #GtkDropTargetAsync::drag-leave signal when the pointer
+ * moves off the widget.
+ *
+ * The ::drag-enter and ::drag-motion handler return a #GdkDragAction
+ * to update the status of the ongoing operation. The ::drop handler
+ * should decide if it ultimately accepts the drop and if it does, it
+ * should initiate the data transfer and finish the operation by calling
+ * gdk_drop_finish().
+ *
+ * Between the ::drag-enter and ::drag-leave signals the widget is a
+ * current drop target, and will receive the %GTK_STATE_FLAG_DROP_ACTIVE
+ * state, which can be used by themes to style the widget as a drop target.
+ */
+
+struct _GtkDropTargetAsync
+{
+  GtkEventController parent_object;
+
+  GdkContentFormats *formats;
+  GdkDragAction actions;
+
+  GdkDrop *drop;
+  gboolean rejected;
+};
+
+struct _GtkDropTargetAsyncClass
+{
+  GtkEventControllerClass parent_class;
+
+  gboolean              (* accept)                              (GtkDropTargetAsync     *self,
+                                                                 GdkDrop                *drop);
+  GdkDragAction         (* drag_enter)                          (GtkDropTargetAsync     *self,
+                                                                 GdkDrop                *drop,
+                                                                 double                  x,
+                                                                 double                  y);
+  GdkDragAction         (* drag_motion)                         (GtkDropTargetAsync     *self,
+                                                                 GdkDrop                *drop,
+                                                                 double                  x,
+                                                                 double                  y);
+  void                  (* drag_leave)                          (GtkDropTargetAsync     *self,
+                                                                 GdkDrop                *drop);
+  gboolean              (* drop)                                (GtkDropTargetAsync     *self,
+                                                                 GdkDrop                *drop,
+                                                                 double                  x,
+                                                                 double                  y);
+};
+
+enum {
+  PROP_0,
+  PROP_ACTIONS,
+  PROP_FORMATS,
+  NUM_PROPERTIES
+};
+
+static GParamSpec *properties[NUM_PROPERTIES];
+
+enum {
+  ACCEPT,
+  DRAG_ENTER,
+  DRAG_MOTION,
+  DRAG_LEAVE,
+  DROP,
+  NUM_SIGNALS
+};
+
+static guint signals[NUM_SIGNALS];
+
+G_DEFINE_TYPE (GtkDropTargetAsync, gtk_drop_target_async, GTK_TYPE_EVENT_CONTROLLER);
+
+static gboolean
+gtk_drop_target_async_accept (GtkDropTargetAsync *self,
+                              GdkDrop            *drop)
+{
+  if ((gdk_drop_get_actions (drop) & self->actions) == 0)
+    return FALSE;
+
+  if (self->formats == NULL)
+    return TRUE;
+
+  return gdk_content_formats_match (self->formats, gdk_drop_get_formats (drop));
+}
+
+static GdkDragAction
+make_action_unique (GdkDragAction actions)
+{
+  if (actions & GDK_ACTION_COPY)
+    return GDK_ACTION_COPY;
+
+  if (actions & GDK_ACTION_MOVE)
+    return GDK_ACTION_MOVE;
+  
+  if (actions & GDK_ACTION_LINK)
+    return GDK_ACTION_LINK;
+
+  return 0;
+}
+  
+static GdkDragAction
+gtk_drop_target_async_drag_enter (GtkDropTargetAsync *self,
+                                  GdkDrop            *drop,
+                                  double              x,
+                                  double              y)
+{
+  return make_action_unique (self->actions & gdk_drop_get_actions (drop));
+}
+
+static GdkDragAction         
+gtk_drop_target_async_drag_motion (GtkDropTargetAsync *self,
+                                   GdkDrop            *drop,
+                                   double              x,
+                                   double              y)
+{
+  return make_action_unique (self->actions & gdk_drop_get_actions (drop));
+}
+
+static gboolean
+gtk_drop_target_async_drop (GtkDropTargetAsync  *self,
+                            GdkDrop             *drop,
+                            double               x,
+                            double               y)
+{
+  return FALSE;
+}
+
+static gboolean
+gtk_drop_target_async_filter_event (GtkEventController *controller,
+                                    GdkEvent           *event)
+{
+  switch ((int)gdk_event_get_event_type (event))
+    {
+    case GDK_DRAG_ENTER:
+    case GDK_DRAG_LEAVE:
+    case GDK_DRAG_MOTION:
+    case GDK_DROP_START:
+      return GTK_EVENT_CONTROLLER_CLASS (gtk_drop_target_async_parent_class)->filter_event (controller, 
event);
+
+    default:;
+    }
+
+  return TRUE;
+}
+
+static gboolean
+gtk_drop_target_async_handle_event (GtkEventController *controller,
+                                    GdkEvent           *event,
+                                    double              x,
+                                    double              y)
+{
+  GtkDropTargetAsync *self = GTK_DROP_TARGET_ASYNC (controller);
+  GdkDrop *drop;
+
+  switch ((int) gdk_event_get_event_type (event))
+    {
+    case GDK_DRAG_MOTION:
+      {
+        GtkWidget *widget = gtk_event_controller_get_widget (controller);
+        GdkDragAction preferred_action;
+
+        drop = gdk_drag_event_get_drop (event);
+        /* sanity check */
+        g_return_val_if_fail (self->drop == drop, FALSE);
+        if (self->rejected)
+          return FALSE;
+
+        g_signal_emit (self, signals[DRAG_MOTION], 0, drop, x, y, &preferred_action);
+        if (preferred_action &&
+            gtk_drop_status (self->drop, self->actions, preferred_action))
+          {
+            gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE, FALSE);
+          }
+        else
+          {
+            gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE);
+          }
+      }
+      return FALSE;
+
+    case GDK_DROP_START:
+      {
+        gboolean handled;
+
+        drop = gdk_drag_event_get_drop (event);
+        /* sanity check */
+        g_return_val_if_fail (self->drop == drop, FALSE);
+        if (self->rejected)
+          return FALSE;
+
+        g_signal_emit (self, signals[DROP], 0, self->drop, x, y, &handled);
+        return handled;
+      }
+
+    default:
+      return FALSE;
+    }
+}
+
+static void
+gtk_drop_target_async_handle_crossing (GtkEventController    *controller,
+                                 const GtkCrossingData *crossing,
+                                 double                 x,
+                                 double                 y)
+{
+  GtkDropTargetAsync *self = GTK_DROP_TARGET_ASYNC (controller);
+  GtkWidget *widget = gtk_event_controller_get_widget (controller);
+
+  if (crossing->type != GTK_CROSSING_DROP)
+    return;
+
+  /* sanity check */
+  g_warn_if_fail (self->drop == NULL || self->drop == crossing->drop);
+
+  if (crossing->direction == GTK_CROSSING_IN)
+    {
+      gboolean accept = FALSE;
+      GdkDragAction preferred_action;
+
+      if (self->drop != NULL)
+        return;
+
+      self->drop = g_object_ref (crossing->drop);
+
+      g_signal_emit (self, signals[ACCEPT], 0, self->drop, &accept);
+      self->rejected = !accept;
+      if (self->rejected)
+        return;
+
+      g_signal_emit (self, signals[DRAG_ENTER], 0, self->drop, x, y, &preferred_action);
+      if (preferred_action &&
+          gtk_drop_status (self->drop, self->actions, preferred_action))
+        {
+          gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE, FALSE);
+        }
+    }
+  else
+    {
+      if (crossing->new_descendent != NULL ||
+          crossing->new_target == widget)
+        return;
+
+      g_signal_emit (self, signals[DRAG_LEAVE], 0, self->drop);
+      g_clear_object (&self->drop);
+      gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE);
+    }
+}
+
+static void
+gtk_drop_target_async_finalize (GObject *object)
+{
+  GtkDropTargetAsync *self = GTK_DROP_TARGET_ASYNC (object);
+
+  g_clear_pointer (&self->formats, gdk_content_formats_unref);
+
+  G_OBJECT_CLASS (gtk_drop_target_async_parent_class)->finalize (object);
+}
+
+static void
+gtk_drop_target_async_set_property (GObject      *object,
+                                    guint         prop_id,
+                                    const GValue *value,
+                                    GParamSpec   *pspec)
+{
+  GtkDropTargetAsync *self = GTK_DROP_TARGET_ASYNC (object);
+
+  switch (prop_id)
+    {
+    case PROP_ACTIONS:
+      gtk_drop_target_async_set_actions (self, g_value_get_flags (value));
+      break;
+
+    case PROP_FORMATS:
+      gtk_drop_target_async_set_formats (self, g_value_get_boxed (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtk_drop_target_async_get_property (GObject    *object,
+                            guint       prop_id,
+                            GValue     *value,
+                            GParamSpec *pspec)
+{
+  GtkDropTargetAsync *self = GTK_DROP_TARGET_ASYNC (object);
+
+  switch (prop_id)
+    {
+    case PROP_ACTIONS:
+      g_value_set_flags (value, gtk_drop_target_async_get_actions (self));
+      break;
+
+    case PROP_FORMATS:
+      g_value_set_boxed (value, gtk_drop_target_async_get_formats (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtk_drop_target_async_class_init (GtkDropTargetAsyncClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+  GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (class);
+
+  object_class->finalize = gtk_drop_target_async_finalize;
+  object_class->set_property = gtk_drop_target_async_set_property;
+  object_class->get_property = gtk_drop_target_async_get_property;
+
+  controller_class->handle_event = gtk_drop_target_async_handle_event;
+  controller_class->filter_event = gtk_drop_target_async_filter_event;
+  controller_class->handle_crossing = gtk_drop_target_async_handle_crossing;
+
+  class->accept = gtk_drop_target_async_accept;
+  class->drag_enter = gtk_drop_target_async_drag_enter;
+  class->drag_motion = gtk_drop_target_async_drag_motion;
+  class->drop = gtk_drop_target_async_drop;
+
+  /**
+   * GtkDropTargetAsync:actions:
+   *
+   * The #GdkDragActions that this drop target supports
+   */ 
+  properties[PROP_ACTIONS] =
+       g_param_spec_flags ("actions", P_("Actions"), P_("Actions"),
+                           GDK_TYPE_DRAG_ACTION, 0,
+                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
+
+  /**
+   * GtkDropTargetAsync:formats:
+   *
+   * The #GdkContentFormats that determines the supported data formats
+   */
+  properties[PROP_FORMATS] =
+       g_param_spec_boxed ("formats", P_("Formats"), P_("Formats"),
+                           GDK_TYPE_CONTENT_FORMATS,
+                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
+
+  g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
+
+ /**
+   * GtkWidget::accept:
+   * @self: the #GtkDropTargetAsync
+   * @drop: the #GdkDrop
+   *
+   * The ::accept signal is emitted on the drop site when a drop operation
+   * is about to begin.  
+   * If the drop is not accepted, %FALSE will be returned and the drop target
+   * will ignore the drop. If %TRUE is returned, the drop is accepted for now
+   * but may be rejected later via a call to gtk_drop_target_reject() or
+   * ultimately by returning %FALSE from GtkDropTarget::drop
+   *
+   * The default handler for this signal decides whether to accept the drop
+   * based on the formats provided by the @drop.
+   *
+   * If the decision whether the drop will be accepted or rejected needs
+   * further procesing, such as inspecting the data, this function should
+   * return %TRUE and proceed as is @drop was accepted and if it decides to
+   * reject the drop later, it should call gtk_drop_target_reject_drop().
+   *
+   * Returns: %TRUE if @drop is accepted
+   */
+  signals[ACCEPT] =
+      g_signal_new (I_("accept"),
+                    G_TYPE_FROM_CLASS (class),
+                    G_SIGNAL_RUN_LAST,
+                    G_STRUCT_OFFSET (GtkDropTargetAsyncClass, accept),
+                    g_signal_accumulator_first_wins, NULL,
+                    NULL,
+                    G_TYPE_BOOLEAN, 1,
+                    GDK_TYPE_DROP);
+
+  /**
+   * GtkDropTargetAsync::drag-enter:
+   * @self: the #GtkDropTargetAsync
+   * @drop: the #GdkDrop
+   * @x: the x coordinate of the current pointer position
+   * @y: the y coordinate of the current pointer position
+   *
+   * The ::drag-enter signal is emitted on the drop site when the pointer
+   * enters the widget. It can be used to set up custom highlighting.
+   *
+   * Returns: Preferred action for this drag operation. 
+   */
+  signals[DRAG_ENTER] =
+      g_signal_new (I_("drag-enter"),
+                    G_TYPE_FROM_CLASS (class),
+                    G_SIGNAL_RUN_LAST,
+                    G_STRUCT_OFFSET (GtkDropTargetAsyncClass, drag_enter),
+                    g_signal_accumulator_first_wins, NULL,
+                    NULL,
+                    GDK_TYPE_DRAG_ACTION, 3,
+                    GDK_TYPE_DROP, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
+
+  /**
+   * GtkDropTargetAsync::drag-motion:
+   * @self: the #GtkDropTargetAsync
+   * @drop: the #GdkDrop
+   * @x: the x coordinate of the current pointer position
+   * @y: the y coordinate of the current pointer position
+   *
+   * The ::drag-motion signal is emitted while the pointer is moving
+   * over the drop target.
+   *
+   * Returns: Preferred action for this drag operation. 
+   */
+  signals[DRAG_MOTION] =
+      g_signal_new (I_("drag-motion"),
+                    G_TYPE_FROM_CLASS (class),
+                    G_SIGNAL_RUN_LAST,
+                    G_STRUCT_OFFSET (GtkDropTargetAsyncClass, drag_motion),
+                    g_signal_accumulator_first_wins, NULL,
+                    NULL,
+                    GDK_TYPE_DRAG_ACTION, 3,
+                    GDK_TYPE_DROP, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
+
+  /**
+   * GtkDropTargetAsync::drag-leave:
+   * @self: the #GtkDropTargetAsync
+   * @drop: the #GdkDrop
+   *
+   * The ::drag-leave signal is emitted on the drop site when the pointer
+   * leaves the widget. Its main purpose it to undo things done in
+   * #GtkDropTargetAsync::drag-enter.
+   */
+  signals[DRAG_LEAVE] =
+      g_signal_new (I_("drag-leave"),
+                    G_TYPE_FROM_CLASS (class),
+                    G_SIGNAL_RUN_LAST,
+                    G_STRUCT_OFFSET (GtkDropTargetAsyncClass, drag_leave),
+                    NULL, NULL,
+                    NULL,
+                    G_TYPE_NONE, 1,
+                    GDK_TYPE_DROP);
+
+  /**
+   * GtkDropTargetAsync::drop:
+   * @self: the #GtkDropTargetAsync
+   * @drop: the #GdkDrop
+   * @x: the x coordinate of the current pointer position
+   * @y: the y coordinate of the current pointer position
+   *
+   * The ::drop signal is emitted on the drop site when the user drops
+   * the data onto the widget. The signal handler must determine whether
+   * the pointer position is in a drop zone or not. If it is not in a drop
+   * zone, it returns %FALSE and no further processing is necessary.
+   *
+   * Otherwise, the handler returns %TRUE. In this case, this handler will
+   * accept the drop. The handler must ensure that gdk_drop_finish() is
+   * called to let the source know that the drop is done. The call to
+   * gtk_drag_finish() must only be done when all data has been received.
+   *
+   * To receive the data, use one of the read functions provides by #GdkDrop
+   * such as gdk_drop_read_async() or gdk_drop_read_value_async().
+   *
+   * Returns: whether the drop is accepted at the given pointer position
+   */
+  signals[DROP] =
+      g_signal_new (I_("drop"),
+                    G_TYPE_FROM_CLASS (class),
+                    G_SIGNAL_RUN_LAST,
+                    0,
+                    g_signal_accumulator_first_wins, NULL,
+                    NULL,
+                    G_TYPE_BOOLEAN, 3,
+                    GDK_TYPE_DROP, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
+}
+
+static void
+gtk_drop_target_async_init (GtkDropTargetAsync *self)
+{
+}
+
+/**
+ * gtk_drop_target_async_new:
+ * @formats: (nullable) (transfer full): the supported data formats
+ * @actions: the supported actions
+ *
+ * Creates a new #GtkDropTargetAsync object.
+ *
+ * Returns: the new #GtkDropTargetAsync
+ */
+GtkDropTargetAsync *
+gtk_drop_target_async_new (GdkContentFormats *formats,
+                     GdkDragAction      actions)
+{
+  GtkDropTargetAsync *result;
+
+  result = g_object_new (GTK_TYPE_DROP_TARGET_ASYNC,
+                         "formats", formats,
+                         "actions", actions,
+                         NULL);
+
+  g_clear_pointer (&formats, gdk_content_formats_unref);
+
+  return result;
+}
+
+/**
+ * gtk_drop_target_async_set_formats:
+ * @self: a #GtkDropTargetAsync
+ * @formats: (nullable): the supported data formats or %NULL for
+ *     any format.
+ *
+ * Sets the data formats that this drop target will accept.
+ */
+void
+gtk_drop_target_async_set_formats (GtkDropTargetAsync *self,
+                                   GdkContentFormats  *formats)
+{
+  g_return_if_fail (GTK_IS_DROP_TARGET_ASYNC (self));
+
+  if (self->formats == formats)
+    return;
+
+  if (self->formats)
+    gdk_content_formats_unref (self->formats);
+
+  self->formats = formats;
+
+  if (self->formats)
+    gdk_content_formats_ref (self->formats);
+
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FORMATS]);
+}
+
+/**
+ * gtk_drop_target_async_get_formats:
+ * @self: a #GtkDropTargetAsync
+ *
+ * Gets the data formats that this drop target accepts.
+ *
+ * If the result is %NULL, all formats are expected to be supported.
+ *
+ * Returns: (nullable): the supported data formats
+ */
+GdkContentFormats *
+gtk_drop_target_async_get_formats (GtkDropTargetAsync *self)
+{
+  g_return_val_if_fail (GTK_IS_DROP_TARGET_ASYNC (self), NULL);
+  
+  return self->formats;
+}
+
+/**
+ * gtk_drop_target_async_set_actions:
+ * @self: a #GtkDropTargetAsync
+ * @actions: the supported actions
+ *
+ * Sets the actions that this drop target supports.
+ */
+void
+gtk_drop_target_async_set_actions (GtkDropTargetAsync *self,
+                                   GdkDragAction       actions)
+{
+  g_return_if_fail (GTK_IS_DROP_TARGET_ASYNC (self));
+  
+  if (self->actions == actions)
+    return;
+
+  self->actions = actions;
+
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACTIONS]);
+}
+
+/**
+ * gtk_drop_target_async_get_actions:
+ * @self: a #GtkDropTargetAsync
+ *
+ * Gets the actions that this drop target supports.
+ *
+ * Returns: the actions that this drop target supports
+ */
+GdkDragAction
+gtk_drop_target_async_get_actions (GtkDropTargetAsync *self)
+{
+  g_return_val_if_fail (GTK_IS_DROP_TARGET_ASYNC (self), 0);
+
+  return self->actions;
+}
+
+/**
+ * gtk_drop_target_async_reject_drop:
+ * @self: a #GtkDropTargetAsync
+ * @drop: the #GdkDrop of an ongoing drag operation
+ *
+ * Sets the @drop as not accepted on this drag site.
+ *
+ * This function should be used when delaying the decision
+ * on whether to accept a drag or not until after reading
+ * the data.
+ */
+void
+gtk_drop_target_async_reject_drop (GtkDropTargetAsync *self,
+                                   GdkDrop            *drop)
+{
+  g_return_if_fail (GTK_IS_DROP_TARGET_ASYNC (self));
+  g_return_if_fail (GDK_IS_DROP (drop));
+  g_return_if_fail (self->drop == drop);
+
+  if (self->rejected)
+    return;
+
+  self->rejected = TRUE;
+  gtk_widget_unset_state_flags (gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (self)),
+                                GTK_STATE_FLAG_DROP_ACTIVE);
+}
diff --git a/gtk/gtkdroptargetasync.h b/gtk/gtkdroptargetasync.h
new file mode 100644
index 0000000000..6e9578ab53
--- /dev/null
+++ b/gtk/gtkdroptargetasync.h
@@ -0,0 +1,77 @@
+/* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * 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/>.
+ */
+
+/*
+ * 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_DROP_TARGET_ASYNC_H__
+#define __GTK_DROP_TARGET_ASYNC_H__
+
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#include <gtk/gtkwidget.h>
+
+
+G_BEGIN_DECLS
+
+typedef struct _GtkDropTargetAsync GtkDropTargetAsync;
+typedef struct _GtkDropTargetAsyncClass GtkDropTargetAsyncClass;
+
+
+#define GTK_TYPE_DROP_TARGET_ASYNC         (gtk_drop_target_async_get_type ())
+#define GTK_DROP_TARGET_ASYNC(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_DROP_TARGET_ASYNC, 
GtkDropTargetAsync))
+#define GTK_DROP_TARGET_ASYNC_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_DROP_TARGET_ASYNC, 
GtkDropTargetAsyncClass))
+#define GTK_IS_DROP_TARGET_ASYNC(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_DROP_TARGET_ASYNC))
+#define GTK_IS_DROP_TARGET_ASYNC_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_DROP_TARGET_ASYNC))
+#define GTK_DROP_TARGET_ASYNC_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_DROP_TARGET_ASYNC, 
GtkDropTargetAsyncClass))
+
+
+GDK_AVAILABLE_IN_ALL
+GType                   gtk_drop_target_async_get_type          (void) G_GNUC_CONST;
+
+GDK_AVAILABLE_IN_ALL
+GtkDropTargetAsync *    gtk_drop_target_async_new               (GdkContentFormats      *formats,
+                                                                 GdkDragAction           actions);
+
+GDK_AVAILABLE_IN_ALL
+void                    gtk_drop_target_async_set_formats       (GtkDropTargetAsync     *self,
+                                                                 GdkContentFormats      *formats);
+GDK_AVAILABLE_IN_ALL
+GdkContentFormats *     gtk_drop_target_async_get_formats       (GtkDropTargetAsync     *self);
+
+GDK_AVAILABLE_IN_ALL
+void                    gtk_drop_target_async_set_actions       (GtkDropTargetAsync     *self,
+                                                                 GdkDragAction           actions);
+GDK_AVAILABLE_IN_ALL
+GdkDragAction           gtk_drop_target_async_get_actions       (GtkDropTargetAsync     *self);
+
+GDK_AVAILABLE_IN_ALL
+void                    gtk_drop_target_async_reject_drop       (GtkDropTargetAsync     *self,
+                                                                 GdkDrop                *drop);
+
+
+G_END_DECLS
+
+#endif /* __GTK_DROP_TARGET_ASYNC_H__ */
diff --git a/gtk/gtkfilechooserbutton.c b/gtk/gtkfilechooserbutton.c
index f73f52add8..ce15326723 100644
--- a/gtk/gtkfilechooserbutton.c
+++ b/gtk/gtkfilechooserbutton.c
@@ -37,7 +37,7 @@
 #include "gtkcellrendererpixbuf.h"
 #include "gtkcombobox.h"
 #include "gtkcssiconthemevalueprivate.h"
-#include "gtkdragdest.h"
+#include "gtkdroptarget.h"
 #include "gtkicontheme.h"
 #include "gtkimage.h"
 #include "gtklabel.h"
@@ -268,11 +268,6 @@ static void     gtk_file_chooser_button_finalize           (GObject          *ob
 
 /* GtkWidget Functions */
 static void     gtk_file_chooser_button_destroy            (GtkWidget        *widget);
-static gboolean gtk_file_chooser_button_drag_drop          (GtkDropTarget    *dest,
-                                                            GdkDrop          *drop,
-                                                            int               x,
-                                                            int               y,
-                                                           GtkWidget        *widget);
 static void     gtk_file_chooser_button_show               (GtkWidget        *widget);
 static void     gtk_file_chooser_button_hide               (GtkWidget        *widget);
 static void     gtk_file_chooser_button_root               (GtkWidget *widget);
@@ -350,6 +345,115 @@ G_DEFINE_TYPE_WITH_CODE (GtkFileChooserButton, gtk_file_chooser_button, GTK_TYPE
                          G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_CHOOSER,
                                                 gtk_file_chooser_button_file_chooser_iface_init))
 
+struct DndSelectFolderData
+{
+  GtkFileSystem *file_system;
+  GtkFileChooserButton *button;
+  GtkFileChooserAction action;
+  GFile *file;
+  gchar **uris;
+  guint i;
+  gboolean selected;
+};
+
+static void
+dnd_select_folder_get_info_cb (GCancellable *cancellable,
+                              GFileInfo    *info,
+                              const GError *error,
+                              gpointer      user_data)
+{
+  struct DndSelectFolderData *data = user_data;
+  GtkFileChooserButton *button = data->button;
+  GtkFileChooserButtonPrivate *priv = gtk_file_chooser_button_get_instance_private (button);
+  gboolean cancelled = g_cancellable_is_cancelled (cancellable);
+
+  if (cancellable != priv->dnd_select_folder_cancellable)
+    {
+      g_object_unref (data->button);
+      g_object_unref (data->file);
+      g_strfreev (data->uris);
+      g_free (data);
+
+      g_object_unref (cancellable);
+      return;
+    }
+
+  priv->dnd_select_folder_cancellable = NULL;
+
+  if (!cancelled && !error && info != NULL)
+    {
+      gboolean is_folder;
+
+      is_folder = _gtk_file_info_consider_as_directory (info);
+
+      data->selected =
+       (((data->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER && is_folder) ||
+         (data->action == GTK_FILE_CHOOSER_ACTION_OPEN && !is_folder)) &&
+        gtk_file_chooser_select_file (GTK_FILE_CHOOSER (data->button), data->file, NULL));
+    }
+  else
+    data->selected = FALSE;
+
+  if (data->selected || data->uris[++data->i] == NULL)
+    {
+      g_signal_emit (data->button, file_chooser_button_signals[FILE_SET], 0);
+
+      g_object_unref (data->button);
+      g_object_unref (data->file);
+      g_strfreev (data->uris);
+      g_free (data);
+
+      g_object_unref (cancellable);
+      return;
+    }
+
+  if (data->file)
+    g_object_unref (data->file);
+
+  data->file = g_file_new_for_uri (data->uris[data->i]);
+
+  priv->dnd_select_folder_cancellable =
+    _gtk_file_system_get_info (data->file_system, data->file,
+                               "standard::type",
+                               dnd_select_folder_get_info_cb, user_data);
+
+  g_object_unref (cancellable);
+}
+
+static gboolean
+gtk_file_chooser_button_drop (GtkDropTarget        *target,
+                              const GValue         *value,
+                              double                x,
+                              double                y,
+                              GtkFileChooserButton *button)
+{
+  GtkFileChooserButtonPrivate *priv = gtk_file_chooser_button_get_instance_private (button);
+  struct DndSelectFolderData *info;
+  GFile *file;
+
+  file = g_value_get_object (value);
+
+  info = g_new0 (struct DndSelectFolderData, 1);
+  info->button = g_object_ref (button);
+  info->i = 0;
+  info->uris = g_new0 (char *, 2);
+  info->selected = FALSE;
+  info->file_system = priv->fs;
+  g_object_get (priv->chooser, "action", &info->action, NULL);
+
+  info->file = g_object_ref (file);
+
+  if (priv->dnd_select_folder_cancellable)
+    g_cancellable_cancel (priv->dnd_select_folder_cancellable);
+
+  priv->dnd_select_folder_cancellable =
+    _gtk_file_system_get_info (priv->fs, info->file,
+                               "standard::type",
+                               dnd_select_folder_get_info_cb, info);
+
+  return TRUE;
+}
+
 static void
 gtk_file_chooser_button_class_init (GtkFileChooserButtonClass * class)
 {
@@ -441,8 +545,7 @@ gtk_file_chooser_button_init (GtkFileChooserButton *button)
   GtkFileChooserButtonPrivate *priv = gtk_file_chooser_button_get_instance_private (button);
   GtkWidget *box;
   GtkWidget *icon;
-  GdkContentFormatsBuilder *builder;
-  GtkDropTarget *dest;
+  GtkDropTarget *target;
 
   priv->button = gtk_button_new ();
   g_signal_connect (priv->button, "clicked", G_CALLBACK (button_clicked_cb), button);
@@ -494,13 +597,9 @@ gtk_file_chooser_button_init (GtkFileChooserButton *button)
                                      NULL, NULL);
 
   /* DnD */
-  builder = gdk_content_formats_builder_new ();
-  gdk_content_formats_builder_add_gtype (builder, G_TYPE_STRING);
-  gdk_content_formats_builder_add_gtype (builder, GDK_TYPE_FILE_LIST);
-  dest = gtk_drop_target_new (gdk_content_formats_builder_free_to_formats (builder),
-                              GDK_ACTION_COPY);
-  g_signal_connect (dest, "drag-drop", G_CALLBACK (gtk_file_chooser_button_drag_drop), button);
-  gtk_widget_add_controller (GTK_WIDGET (button), GTK_EVENT_CONTROLLER (dest));
+  target = gtk_drop_target_new (G_TYPE_FILE, GDK_ACTION_COPY);
+  g_signal_connect (target, "drop", G_CALLBACK (gtk_file_chooser_button_drop), button);
+  gtk_widget_add_controller (GTK_WIDGET (button), GTK_EVENT_CONTROLLER (target));
 }
 
 
@@ -1045,169 +1144,6 @@ gtk_file_chooser_button_destroy (GtkWidget *widget)
   GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->destroy (widget);
 }
 
-struct DndSelectFolderData
-{
-  GtkFileSystem *file_system;
-  GtkFileChooserButton *button;
-  GtkFileChooserAction action;
-  GFile *file;
-  gchar **uris;
-  guint i;
-  gboolean selected;
-};
-
-static void
-dnd_select_folder_get_info_cb (GCancellable *cancellable,
-                              GFileInfo    *info,
-                              const GError *error,
-                              gpointer      user_data)
-{
-  struct DndSelectFolderData *data = user_data;
-  GtkFileChooserButton *button = data->button;
-  GtkFileChooserButtonPrivate *priv = gtk_file_chooser_button_get_instance_private (button);
-  gboolean cancelled = g_cancellable_is_cancelled (cancellable);
-
-  if (cancellable != priv->dnd_select_folder_cancellable)
-    {
-      g_object_unref (data->button);
-      g_object_unref (data->file);
-      g_strfreev (data->uris);
-      g_free (data);
-
-      g_object_unref (cancellable);
-      return;
-    }
-
-  priv->dnd_select_folder_cancellable = NULL;
-
-  if (!cancelled && !error && info != NULL)
-    {
-      gboolean is_folder;
-
-      is_folder = _gtk_file_info_consider_as_directory (info);
-
-      data->selected =
-       (((data->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER && is_folder) ||
-         (data->action == GTK_FILE_CHOOSER_ACTION_OPEN && !is_folder)) &&
-        gtk_file_chooser_select_file (GTK_FILE_CHOOSER (data->button), data->file, NULL));
-    }
-  else
-    data->selected = FALSE;
-
-  if (data->selected || data->uris[++data->i] == NULL)
-    {
-      g_signal_emit (data->button, file_chooser_button_signals[FILE_SET], 0);
-
-      g_object_unref (data->button);
-      g_object_unref (data->file);
-      g_strfreev (data->uris);
-      g_free (data);
-
-      g_object_unref (cancellable);
-      return;
-    }
-
-  if (data->file)
-    g_object_unref (data->file);
-
-  data->file = g_file_new_for_uri (data->uris[data->i]);
-
-  priv->dnd_select_folder_cancellable =
-    _gtk_file_system_get_info (data->file_system, data->file,
-                               "standard::type",
-                               dnd_select_folder_get_info_cb, user_data);
-
-  g_object_unref (cancellable);
-}
-
-static void
-dnd_select_file (GtkFileChooserButton *button,
-                 GFile                *file)
-{
-  GtkFileChooserButtonPrivate *priv = gtk_file_chooser_button_get_instance_private (button);
-  struct DndSelectFolderData *info;
-
-  info = g_new0 (struct DndSelectFolderData, 1);
-  info->button = g_object_ref (button);
-  info->i = 0;
-  info->uris = g_new0 (char *, 2);
-  info->selected = FALSE;
-  info->file_system = priv->fs;
-  g_object_get (priv->chooser, "action", &info->action, NULL);
-
-  info->file = g_object_ref (file);
-
-  if (priv->dnd_select_folder_cancellable)
-    g_cancellable_cancel (priv->dnd_select_folder_cancellable);
-
-  priv->dnd_select_folder_cancellable =
-    _gtk_file_system_get_info (priv->fs, info->file,
-                               "standard::type",
-                               dnd_select_folder_get_info_cb, info);
-}
-
-static void
-got_file (GObject      *source,
-          GAsyncResult *result,
-          gpointer      data)
-{
-  GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (data);
-  GdkDrop *drop = GDK_DROP (source);
-  const GValue *value;
-
-  value = gdk_drop_read_value_finish (drop, result, NULL);
-  if (value)
-    {
-      GFile *file;
-
-      file = g_value_get_object (value);
-      dnd_select_file (button, file);
-    }
-}
-
-static void
-got_text (GObject      *source,
-          GAsyncResult *result,
-          gpointer      data)
-{
-  GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (data);
-  GdkDrop *drop = GDK_DROP (source);
-  char *str;
-
-  str = gdk_drop_read_text_finish (drop, result, NULL);
-  if (str)
-    {
-      GFile *file;
-
-      file = g_file_new_for_uri (str);
-      dnd_select_file (button, file);
-      g_object_unref (file);
-    }
-
-}
-
-static gboolean
-gtk_file_chooser_button_drag_drop (GtkDropTarget *dest,
-                                   GdkDrop       *drop,
-                                   int            x,
-                                   int            y,
-                                   GtkWidget     *button)
-{
-  if (gdk_drop_has_value (drop, G_TYPE_FILE))
-    {
-      gdk_drop_read_value_async (drop, G_TYPE_FILE, G_PRIORITY_DEFAULT, NULL, got_file, button);
-      return TRUE;
-    }
-  else
-    {
-      gdk_drop_read_text_async (drop, NULL, got_text, button);
-      return TRUE;
-    }
-
-  return FALSE;
-
-}
-
 static void
 gtk_file_chooser_button_show (GtkWidget *widget)
 {
diff --git a/gtk/gtkfilechooserwidget.c b/gtk/gtkfilechooserwidget.c
index 9460663468..1b6a03826f 100644
--- a/gtk/gtkfilechooserwidget.c
+++ b/gtk/gtkfilechooserwidget.c
@@ -31,7 +31,7 @@
 #include "gtkcomboboxtext.h"
 #include "gtkcssnumbervalueprivate.h"
 #include "gtkdragsource.h"
-#include "gtkdragdest.h"
+#include "gtkdroptarget.h"
 #include "gtkentry.h"
 #include "gtkfilechooserprivate.h"
 #include "gtkfilechooserdialog.h"
@@ -1839,25 +1839,17 @@ out:
   g_object_unref (cancellable);
 }
 
-static void
-file_list_drag_data_received_cb (GObject      *source,
-                                 GAsyncResult *result,
-                                 gpointer      user_data)
+static gboolean
+file_list_drag_drop_cb (GtkDropTarget        *dest,
+                        const GValue         *value,
+                        double                x,
+                        double                y,
+                        GtkFileChooserWidget *impl)
 {
-  GtkFileChooserWidget *impl = GTK_FILE_CHOOSER_WIDGET (user_data);
   GtkFileChooserWidgetPrivate *priv = gtk_file_chooser_widget_get_instance_private (impl);
-  GdkDrop *drop = GDK_DROP (source);
   GSList *files;
-  const GValue *value;
   FileListDragData *data;
 
-  value = gdk_drop_read_value_finish (drop, result, NULL);
-  if (value == NULL)
-    {
-      gdk_drop_finish (drop, 0);
-      return;
-    }
-
   files = g_value_get_boxed (value);
 
   data = g_new0 (FileListDragData, 1);
@@ -1873,30 +1865,6 @@ file_list_drag_data_received_cb (GObject      *source,
                                file_list_drag_data_received_get_info_cb,
                                    data);
 
-  gdk_drop_finish (drop, gdk_drop_get_actions (drop));
-}
-
-/* Don't do anything with the drag_drop signal */
-static gboolean
-file_list_drag_drop_cb (GtkDropTarget        *dest,
-                        GdkDrop              *drop,
-                        int                   x,
-                        int                   y,
-                        GtkFileChooserWidget *impl)
-{
-  gdk_drop_read_value_async (drop, GDK_TYPE_FILE_LIST, G_PRIORITY_DEFAULT, NULL, 
file_list_drag_data_received_cb, impl);
-
-  return TRUE;
-}
-
-/* Disable the normal tree drag motion handler, it makes it look like you're
-   dropping the dragged item onto a tree item */
-static gboolean
-file_list_drag_accept_cb (GtkDropTarget        *dest,
-                          GdkDrop              *drop,
-                          GtkFileChooserWidget *impl)
-{
-  g_signal_stop_emission_by_name (dest, "accept");
   return TRUE;
 }
 
@@ -7954,7 +7922,7 @@ post_process_ui (GtkFileChooserWidget *impl)
   GtkCellRenderer  *cell;
   GList            *cells;
   GFile            *file;
-  GtkDropTarget *dest;
+  GtkDropTarget *target;
 
   /* Setup file list treeview */
   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view));
@@ -7966,11 +7934,9 @@ post_process_ui (GtkFileChooserWidget *impl)
                                           gdk_content_formats_new_for_gtype (GDK_TYPE_FILE_LIST),
                                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
   
-  dest = gtk_drop_target_new (gdk_content_formats_new_for_gtype (GDK_TYPE_FILE_LIST),
-                              GDK_ACTION_COPY | GDK_ACTION_MOVE);
-  g_signal_connect (dest, "accept", G_CALLBACK (file_list_drag_accept_cb), impl);
-  g_signal_connect (dest, "drag-drop", G_CALLBACK (file_list_drag_drop_cb), impl);
-  gtk_widget_add_controller (priv->browse_files_tree_view, GTK_EVENT_CONTROLLER (dest));
+  target = gtk_drop_target_new (GDK_TYPE_FILE_LIST, GDK_ACTION_COPY | GDK_ACTION_MOVE);
+  g_signal_connect (target, "drop", G_CALLBACK (file_list_drag_drop_cb), impl);
+  gtk_widget_add_controller (priv->browse_files_tree_view, GTK_EVENT_CONTROLLER (target));
 
   /* File browser treemodel columns are shared between GtkFileChooser implementations,
    * so we don't set cell renderer attributes in GtkBuilder, but rather keep that
diff --git a/gtk/gtkiconview.c b/gtk/gtkiconview.c
index 9400fdd24b..acaf40f10d 100644
--- a/gtk/gtkiconview.c
+++ b/gtk/gtkiconview.c
@@ -30,7 +30,6 @@
 #include "gtkcellrenderertext.h"
 #include "gtkcombobox.h"
 #include "gtkcssnodeprivate.h"
-#include "gtkdragdest.h"
 #include "gtkdragsource.h"
 #include "gtkentry.h"
 #include "gtkintl.h"
@@ -48,7 +47,6 @@
 #include "gtkwindow.h"
 #include "gtkeventcontrollerkey.h"
 #include "gtkdragsource.h"
-#include "gtkdragdest.h"
 #include "gtkdragicon.h"
 #include "gtknative.h"
 
@@ -287,19 +285,19 @@ static GdkContentProvider * gtk_icon_view_drag_data_get                  (GtkIco
                                                                           GtkTreePath            
*source_row);
 
 /* Target side drag signals */
-static void     gtk_icon_view_drag_leave         (GtkDropTarget    *dest,
-                                                  GdkDrop          *drop,
-                                                  GtkIconView      *icon_view);
-static void     gtk_icon_view_drag_motion        (GtkDropTarget    *dest,
-                                                  GdkDrop          *drop,
-                                                  int               x,
-                                                  int               y,
-                                                  GtkIconView      *icon_view);
-static gboolean gtk_icon_view_drag_drop          (GtkDropTarget    *dest,
-                                                  GdkDrop          *drop,
-                                                  int               x,
-                                                  int               y,
-                                                  GtkIconView      *icon_view);
+static void                 gtk_icon_view_drag_leave                     (GtkDropTargetAsync     *dest,
+                                                                          GdkDrop                *drop,
+                                                                          GtkIconView            *icon_view);
+static GdkDragAction        gtk_icon_view_drag_motion                    (GtkDropTargetAsync     *dest,
+                                                                          GdkDrop                *drop,
+                                                                          double                  x,
+                                                                          double                  y,
+                                                                          GtkIconView            *icon_view);
+static gboolean             gtk_icon_view_drag_drop                      (GtkDropTargetAsync     *dest,
+                                                                          GdkDrop                *drop,
+                                                                          double                  x,
+                                                                          double                  y,
+                                                                          GtkIconView            *icon_view);
 static void     gtk_icon_view_drag_data_received (GObject          *source,
                                                   GAsyncResult     *result,
                                                   gpointer          data);
@@ -5828,12 +5826,12 @@ drag_scroll_timeout (gpointer data)
 }
 
 static gboolean
-set_destination (GtkIconView    *icon_view,
-                GtkDropTarget  *dest,
-                gint            x,
-                gint            y,
-                GdkDragAction  *suggested_action,
-                GType          *target)
+set_destination (GtkIconView        *icon_view,
+                GtkDropTargetAsync *dest,
+                gint                x,
+                gint                y,
+                GdkDragAction      *suggested_action,
+                GType              *target)
 {
   GtkWidget *widget;
   GtkTreePath *path = NULL;
@@ -5863,7 +5861,7 @@ set_destination (GtkIconView    *icon_view,
       return FALSE; /* no longer a drop site */
     }
 
-  formats = gtk_drop_target_get_formats (dest);
+  formats = gtk_drop_target_async_get_formats (dest);
   *target = gdk_content_formats_match_gtype (formats, formats);
   if (*target == G_TYPE_INVALID)
     return FALSE;
@@ -6129,9 +6127,9 @@ gtk_icon_view_dnd_finished_cb (GdkDrag   *drag,
 
 /* Target side drag signals */
 static void
-gtk_icon_view_drag_leave (GtkDropTarget *dest,
-                          GdkDrop       *drop,
-                          GtkIconView   *icon_view)
+gtk_icon_view_drag_leave (GtkDropTargetAsync *dest,
+                          GdkDrop            *drop,
+                          GtkIconView        *icon_view)
 {
   /* unset any highlight row */
   gtk_icon_view_set_drag_dest_item (icon_view,
@@ -6141,24 +6139,22 @@ gtk_icon_view_drag_leave (GtkDropTarget *dest,
   remove_scroll_timeout (icon_view);
 }
 
-static void
-gtk_icon_view_drag_motion (GtkDropTarget *dest,
-                           GdkDrop       *drop,
-                          int            x,
-                          int            y,
-                           GtkIconView   *icon_view)
+static GdkDragAction
+gtk_icon_view_drag_motion (GtkDropTargetAsync *dest,
+                           GdkDrop            *drop,
+                          double              x,
+                          double              y,
+                           GtkIconView        *icon_view)
 {
   GtkTreePath *path = NULL;
   GtkIconViewDropPosition pos;
   GdkDragAction suggested_action = 0;
   GType target;
   gboolean empty;
+  GdkDragAction result;
 
   if (!set_destination (icon_view, dest, x, y, &suggested_action, &target))
-    {
-      gdk_drop_status (drop, 0);
-      return;
-    }
+    return 0;
 
   gtk_icon_view_get_drag_dest_item (icon_view, &path, &pos);
 
@@ -6168,7 +6164,7 @@ gtk_icon_view_drag_motion (GtkDropTarget *dest,
   if (path == NULL && !empty)
     {
       /* Can't drop here. */
-      gdk_drop_status (drop, 0);
+      result = 0;
     }
   else
     {
@@ -6189,20 +6185,22 @@ gtk_icon_view_drag_motion (GtkDropTarget *dest,
       else
         {
           set_status_pending (drop, 0);
-          gdk_drop_status (drop, suggested_action);
         }
+      result = suggested_action;
     }
 
   if (path)
     gtk_tree_path_free (path);
+
+  return result;
 }
 
 static gboolean 
-gtk_icon_view_drag_drop (GtkDropTarget *dest,
-                         GdkDrop       *drop,
-                        int            x,
-                        int            y,
-                         GtkIconView   *icon_view)
+gtk_icon_view_drag_drop (GtkDropTargetAsync *dest,
+                         GdkDrop            *drop,
+                        double              x,
+                        double              y,
+                         GtkIconView        *icon_view)
 {
   GtkTreePath *path;
   GdkDragAction suggested_action = 0;
@@ -6217,7 +6215,7 @@ gtk_icon_view_drag_drop (GtkDropTarget *dest,
   if (!icon_view->priv->dest_set)
     return FALSE;
 
-  if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag-drop"))
+  if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drop"))
     return FALSE;
 
   if (!set_destination (icon_view, dest, x, y, &suggested_action, &target))
@@ -6324,8 +6322,6 @@ gtk_icon_view_drag_data_received (GObject *source,
            suggested_action = 0;
         }
 
-      gdk_drop_status (drop, suggested_action);
-
       if (path)
         gtk_tree_path_free (path);
 
@@ -6407,10 +6403,11 @@ gtk_icon_view_enable_model_drag_dest (GtkIconView       *icon_view,
 
   g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
 
-  icon_view->priv->dest = gtk_drop_target_new (gdk_content_formats_ref (formats), actions);
+  icon_view->priv->dest = gtk_drop_target_async_new (gdk_content_formats_ref (formats), actions);
   g_signal_connect (icon_view->priv->dest, "drag-leave", G_CALLBACK (gtk_icon_view_drag_leave), icon_view);
+  g_signal_connect (icon_view->priv->dest, "drag-enter", G_CALLBACK (gtk_icon_view_drag_motion), icon_view);
   g_signal_connect (icon_view->priv->dest, "drag-motion", G_CALLBACK (gtk_icon_view_drag_motion), icon_view);
-  g_signal_connect (icon_view->priv->dest, "drag-drop", G_CALLBACK (gtk_icon_view_drag_drop), icon_view);
+  g_signal_connect (icon_view->priv->dest, "drop", G_CALLBACK (gtk_icon_view_drag_drop), icon_view);
   gtk_widget_add_controller (GTK_WIDGET (icon_view), GTK_EVENT_CONTROLLER (icon_view->priv->dest));
 
   icon_view->priv->dest_actions = actions;
diff --git a/gtk/gtkiconviewprivate.h b/gtk/gtkiconviewprivate.h
index c45c3ba3e9..1cfd1aa3d8 100644
--- a/gtk/gtkiconviewprivate.h
+++ b/gtk/gtkiconviewprivate.h
@@ -17,10 +17,10 @@
 
 #include "gtk/gtkiconview.h"
 #include "gtk/gtkcssnodeprivate.h"
-#include "gtk/gtkdragdest.h"
-#include "gtk/gtkgestureclick.h"
 #include "gtk/gtkeventcontrollermotion.h"
 #include "gtk/gtkdragsource.h"
+#include "gtk/gtkdroptargetasync.h"
+#include "gtk/gtkgestureclick.h"
 
 #ifndef __GTK_ICON_VIEW_PRIVATE_H__
 #define __GTK_ICON_VIEW_PRIVATE_H__
@@ -135,7 +135,7 @@ struct _GtkIconViewPrivate
   gint press_start_y;
 
   GdkContentFormats *source_formats;
-  GtkDropTarget *dest;
+  GtkDropTargetAsync *dest;
   GtkCssNode *dndnode;
 
   GdkDrag *drag;
diff --git a/gtk/gtklistbox.c b/gtk/gtklistbox.c
index 9db8ee9ac0..ffa4a0112c 100644
--- a/gtk/gtklistbox.c
+++ b/gtk/gtklistbox.c
@@ -25,7 +25,6 @@
 #include "gtkbuildable.h"
 #include "gtkcontainerprivate.h"
 #include "gtkcssnodeprivate.h"
-#include "gtkdragdest.h"
 #include "gtkgestureclick.h"
 #include "gtkintl.h"
 #include "gtkmain.h"
diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c
index fe2f63812f..bc164008bb 100644
--- a/gtk/gtkmain.c
+++ b/gtk/gtkmain.c
@@ -117,7 +117,7 @@
 #include "gtkaccelmapprivate.h"
 #include "gtkbox.h"
 #include "gtkdebug.h"
-#include "gtkdragdestprivate.h"
+#include "gtkdropprivate.h"
 #include "gtkmain.h"
 #include "gtkmediafileprivate.h"
 #include "gtkmodulesprivate.h"
@@ -1550,9 +1550,14 @@ handle_pointing_event (GdkEvent *event)
                                         event, gdk_crossing_event_get_mode (event), NULL);
       break;
     case GDK_DRAG_LEAVE:
-      old_target = update_pointer_focus_state (toplevel, event, NULL);
-      gtk_synthesize_crossing_events (GTK_ROOT (toplevel), GTK_CROSSING_DROP, old_target, NULL,
-                                      event, GDK_CROSSING_NORMAL, gdk_drag_event_get_drop (event));
+      {
+        GdkDrop *drop = gdk_drag_event_get_drop (event);
+        old_target = update_pointer_focus_state (toplevel, event, NULL);
+        gtk_drop_begin_event (drop, GDK_DRAG_LEAVE);
+        gtk_synthesize_crossing_events (GTK_ROOT (toplevel), GTK_CROSSING_DROP, old_target, NULL,
+                                        event, GDK_CROSSING_NORMAL, drop);
+        gtk_drop_end_event (drop);
+      }
       break;
     case GDK_ENTER_NOTIFY:
       if (gdk_crossing_event_get_mode (event) == GDK_CROSSING_GRAB ||
@@ -1586,10 +1591,14 @@ handle_pointing_event (GdkEvent *event)
 
           gtk_window_maybe_update_cursor (toplevel, NULL, device);
         }
-      else if (type == GDK_DRAG_ENTER || type == GDK_DRAG_MOTION || type == GDK_DROP_START)
+      else if ((old_target != target) &&
+               (type == GDK_DRAG_ENTER || type == GDK_DRAG_MOTION || type == GDK_DROP_START))
         {
+          GdkDrop *drop = gdk_drag_event_get_drop (event);
+          gtk_drop_begin_event (drop, type);
           gtk_synthesize_crossing_events (GTK_ROOT (toplevel), GTK_CROSSING_DROP, old_target, target,
                                           event, GDK_CROSSING_NORMAL, gdk_drag_event_get_drop (event));
+          gtk_drop_end_event (drop);
         }
       else if (type == GDK_TOUCH_BEGIN)
         gtk_window_set_pointer_focus_grab (toplevel, device, sequence, target);
@@ -1786,19 +1795,19 @@ gtk_main_do_event (GdkEvent *event)
 
     case GDK_ENTER_NOTIFY:
     case GDK_LEAVE_NOTIFY:
+    case GDK_DRAG_ENTER:
+    case GDK_DRAG_LEAVE:
       /* Crossing event propagation happens during picking */
       break;
 
     case GDK_DRAG_MOTION:
     case GDK_DROP_START:
-      if (gtk_propagate_event (target_widget, event))
-        break;
-      G_GNUC_FALLTHROUGH;
-
-    case GDK_DRAG_ENTER:
-    case GDK_DRAG_LEAVE:
-      /* Crossing event propagation happens during picking */
-      gtk_drag_dest_handle_event (target_widget, event);
+      {
+        GdkDrop *drop = gdk_drag_event_get_drop (event);
+        gtk_drop_begin_event (drop, gdk_event_get_event_type (event));
+        gtk_propagate_event (target_widget, event);
+        gtk_drop_end_event (drop);
+      }
       break;
 
     case GDK_EVENT_LAST:
diff --git a/gtk/gtknotebook.c b/gtk/gtknotebook.c
index 7886f55dd7..92f7651a2e 100644
--- a/gtk/gtknotebook.c
+++ b/gtk/gtknotebook.c
@@ -33,7 +33,7 @@
 #include "gtkbuildable.h"
 #include "gtkbutton.h"
 #include "gtkcssstylepropertyprivate.h"
-#include "gtkdragdest.h"
+#include "gtkdroptarget.h"
 #include "gtkdragicon.h"
 #include "gtkdropcontrollermotion.h"
 #include "gtkeventcontrollermotion.h"
@@ -793,14 +793,15 @@ static void gtk_notebook_dnd_finished_cb     (GdkDrag          *drag,
 static void gtk_notebook_drag_cancel_cb      (GdkDrag          *drag,
                                               GdkDragCancelReason reason,
                                               GtkWidget        *widget);
-static void     gtk_notebook_drag_motion     (GtkDropTarget    *dest,
-                                              GdkDrop          *drop,
-                                              int               x,
-                                              int               y);
+static GdkDragAction gtk_notebook_drag_motion(GtkDropTarget    *dest,
+                                              double            x,
+                                              double            y,
+                                              GtkNotebook      *notebook);
 static gboolean gtk_notebook_drag_drop       (GtkDropTarget    *dest,
-                                              GdkDrop          *drop,
-                                              int               x,
-                                              int               y);
+                                              const GValue     *value,
+                                              double            x,
+                                              double            y,
+                                              GtkNotebook      *notebook);
 
 /*** GtkContainer Methods ***/
 static void gtk_notebook_add                 (GtkContainer     *container,
@@ -1430,9 +1431,10 @@ gtk_notebook_init (GtkNotebook *notebook)
   gtk_widget_set_vexpand (priv->stack_widget, TRUE);
   gtk_widget_set_parent (priv->stack_widget, GTK_WIDGET (notebook));
 
-  dest = gtk_drop_target_new (gdk_content_formats_new_for_gtype (GTK_TYPE_NOTEBOOK_PAGE), GDK_ACTION_MOVE);
-  g_signal_connect (dest, "drag-motion", G_CALLBACK (gtk_notebook_drag_motion), NULL);
-  g_signal_connect (dest, "drag-drop", G_CALLBACK (gtk_notebook_drag_drop), NULL);
+  dest = gtk_drop_target_new (GTK_TYPE_NOTEBOOK_PAGE, GDK_ACTION_MOVE);
+  gtk_drop_target_set_preload (dest, TRUE);
+  g_signal_connect (dest, "motion", G_CALLBACK (gtk_notebook_drag_motion), notebook);
+  g_signal_connect (dest, "drop", G_CALLBACK (gtk_notebook_drag_drop), notebook);
   gtk_widget_add_controller (GTK_WIDGET (priv->tabs_widget), GTK_EVENT_CONTROLLER (dest));
 
   gesture = gtk_gesture_click_new ();
@@ -3294,112 +3296,78 @@ gtk_notebook_switch_page_timeout (gpointer data)
   return FALSE;
 }
 
-static void
-gtk_notebook_drag_motion (GtkDropTarget *dest,
-                          GdkDrop       *drop,
-                          int            x,
-                          int            y)
+static gboolean
+gtk_notebook_can_drag_from (GtkNotebook     *self,
+                            GtkNotebook     *other,
+                            GtkNotebookPage *page)
 {
-  GtkWidget *tabs = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest));
-  GtkWidget *widget = gtk_widget_get_ancestor (tabs, GTK_TYPE_NOTEBOOK);
-  GtkNotebook *notebook = GTK_NOTEBOOK (widget);
-  GtkNotebookPrivate *priv = notebook->priv;
-  GdkContentFormats *formats;
-
-  priv->mouse_x = x;
-  priv->mouse_y = y;
-
-  formats = gtk_drop_target_get_formats (dest);
-  if (gdk_content_formats_contain_gtype (formats, GTK_TYPE_NOTEBOOK_PAGE))
-    {
-      GQuark group, source_group;
-      GtkWidget *source_child;
-      GdkDrag *drag = gdk_drop_get_drag (drop);
-
-      if (!drag)
-        {
-          gdk_drop_status (drop, 0);
-        }
-      else
-        {
-          GtkNotebook *source = GTK_NOTEBOOK (g_object_get_data (G_OBJECT (drag), 
"gtk-notebook-drag-origin"));
+  /* always allow dragging inside self */
+  if (self == other)
+    return TRUE;
 
-          g_assert (source->priv->cur_page != NULL);
-          source_child = source->priv->cur_page->child;
+  /* if the groups don't match, fail */
+  if (self->priv->group == 0 ||
+      self->priv->group != other->priv->group)
+    return FALSE;
 
-          group = notebook->priv->group;
-          source_group = source->priv->group;
+  /* Check that the dragged page is not a parent of the notebook
+   * being dragged into */
+  if (GTK_WIDGET (self) == page->child ||
+      gtk_widget_is_ancestor (GTK_WIDGET (self), GTK_WIDGET (page->child)) ||
+      GTK_WIDGET (self) == page->tab_label ||
+      gtk_widget_is_ancestor (GTK_WIDGET (self), GTK_WIDGET (page->tab_label)))
+    return FALSE;
 
-          if (group != 0 && group == source_group &&
-              !(widget == source_child ||
-              gtk_widget_is_ancestor (widget, source_child)))
-            {
-              gdk_drop_status (drop, GDK_ACTION_MOVE);
-            }
-          else
-            {
-              /* it's a tab, but doesn't share
-               * ID with this notebook */
-              gdk_drop_status (drop, 0);
-            }
-        }
-    }
+  return TRUE;
 }
 
-static void
-got_page (GObject      *source,
-          GAsyncResult *result,
-          gpointer      data)
+static GdkDragAction
+gtk_notebook_drag_motion (GtkDropTarget *dest,
+                          double         x,
+                          double         y,
+                          GtkNotebook   *notebook)
 {
-  GtkNotebook *notebook = GTK_NOTEBOOK (data);
-  GdkDrop *drop = GDK_DROP (source);
-  GdkDrag *drag = gdk_drop_get_drag (drop);
-  GtkWidget *source_widget;
-  const GValue *value;
+  GtkNotebookPrivate *priv = notebook->priv;
+  GdkDrag *drag = gdk_drop_get_drag (gtk_drop_target_get_drop (dest));
+  GtkNotebook *source;
 
-  source_widget = GTK_WIDGET (drag ? g_object_get_data (G_OBJECT (drag), "gtk-notebook-drag-origin") : NULL);
+  priv->mouse_x = x;
+  priv->mouse_y = y;
 
-  value = gdk_drop_read_value_finish (drop, result, NULL);
+  if (!drag)
+    return 0;
 
-  if (value)
-    {
-      GtkNotebookPage *page = g_value_get_object (value);
+  source = GTK_NOTEBOOK (g_object_get_data (G_OBJECT (drag), "gtk-notebook-drag-origin"));
+  g_assert (source->priv->cur_page != NULL);
 
-      do_detach_tab (GTK_NOTEBOOK (source_widget), notebook, page->child);
-      gdk_drop_finish (drop, GDK_ACTION_MOVE);
-    }
-  else
-    gdk_drop_finish (drop, 0);
+  if (!gtk_notebook_can_drag_from (notebook, source, source->priv->cur_page))
+    return 0;
+
+  return GDK_ACTION_MOVE;
 }
 
 static gboolean
 gtk_notebook_drag_drop (GtkDropTarget *dest,
-                        GdkDrop       *drop,
-                        int            x,
-                        int            y)
+                        const GValue  *value,
+                        double         x,
+                        double         y,
+                        GtkNotebook   *self)
 {
-  GtkWidget *tabs = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest));
-  GtkWidget *widget = gtk_widget_get_ancestor (tabs, GTK_TYPE_NOTEBOOK);
-  GtkNotebook *notebook = GTK_NOTEBOOK (widget);
-  GdkDrag *drag = gdk_drop_get_drag (drop);
-  GtkWidget *source_widget;
+  GdkDrag *drag = gdk_drop_get_drag (gtk_drop_target_get_drop (dest));
+  GtkNotebook *source;
+  GtkNotebookPage *page = g_value_get_object (value);
 
-  source_widget = GTK_WIDGET (drag ? g_object_get_data (G_OBJECT (drag), "gtk-notebook-drag-origin") : NULL);
-
-  if (GTK_IS_NOTEBOOK (source_widget) &&
-      (gdk_drop_get_actions (drop) & GDK_ACTION_MOVE))
-    {
-      notebook->priv->mouse_x = x;
-      notebook->priv->mouse_y = y;
+  source = drag ? g_object_get_data (G_OBJECT (drag), "gtk-notebook-drag-origin") : NULL;
 
-      gdk_drop_read_value_async (drop, GTK_TYPE_NOTEBOOK_PAGE, G_PRIORITY_DEFAULT, NULL, got_page, notebook);
+  if (!gtk_notebook_can_drag_from (self, source, source->priv->cur_page))
+    return FALSE;
 
-      return TRUE;
-    }
+  self->priv->mouse_x = x;
+  self->priv->mouse_y = y;
 
-  gdk_drop_finish (drop, 0);
+  do_detach_tab (source, self, page->child);
 
-  return FALSE;
+  return TRUE;
 }
 
 /**
diff --git a/gtk/gtkplacessidebar.c b/gtk/gtkplacessidebar.c
index 925370a6a4..f62569b5f6 100644
--- a/gtk/gtkplacessidebar.c
+++ b/gtk/gtkplacessidebar.c
@@ -50,7 +50,7 @@
 #include "gtklabel.h"
 #include "gtkbutton.h"
 #include "gtklistbox.h"
-#include "gtkdragdest.h"
+#include "gtkdroptarget.h"
 #include "gtkseparator.h"
 #include "gtkentry.h"
 #include "gtkgesturelongpress.h"
@@ -150,8 +150,6 @@ struct _GtkPlacesSidebar {
   GtkWidget *trash_row;
 
   /* DND */
-  GSList    *drag_list; /* list of GFile */
-  gint       drag_data_info;
   gboolean   dragging_over;
   GtkWidget *drag_row;
   gint drag_row_height;
@@ -175,8 +173,6 @@ struct _GtkPlacesSidebar {
   GtkPlacesOpenFlags open_flags;
 
   guint mounting               : 1;
-  guint  drag_data_received    : 1;
-  guint drop_occurred          : 1;
   guint show_recent_set        : 1;
   guint show_recent            : 1;
   guint show_desktop_set       : 1;
@@ -300,13 +296,6 @@ static GMountOperation * get_mount_operation (GtkPlacesSidebar *sidebar);
 static GMountOperation * get_unmount_operation (GtkPlacesSidebar *sidebar);
 
 
-/* Identifiers for target types */
-enum {
-  DND_UNKNOWN,
-  DND_GTK_SIDEBAR_ROW,
-  DND_TEXT_URI_LIST
-};
-
 G_DEFINE_TYPE (GtkPlacesSidebar, gtk_places_sidebar, GTK_TYPE_WIDGET);
 
 static void
@@ -380,18 +369,6 @@ emit_drag_action_requested (GtkPlacesSidebar *sidebar,
   return ret_action;
 }
 
-static GdkDragAction
-emit_drag_action_ask (GtkPlacesSidebar *sidebar,
-                      GdkDragAction     actions)
-{
-  GdkDragAction ret_action = 0;
-
-  g_signal_emit (sidebar, places_sidebar_signals[DRAG_ACTION_ASK], 0,
-                 actions, &ret_action);
-
-  return ret_action;
-}
-
 static void
 emit_drag_perform_drop (GtkPlacesSidebar *sidebar,
                         GFile            *dest_file,
@@ -1513,7 +1490,8 @@ update_places (GtkPlacesSidebar *sidebar)
 
 static gboolean
 check_valid_drop_target (GtkPlacesSidebar *sidebar,
-                         GtkSidebarRow    *row)
+                         GtkSidebarRow    *row,
+                         const GValue     *value)
 {
   GtkPlacesSidebarPlaceType place_type;
   GtkPlacesSidebarSectionType section_type;
@@ -1522,6 +1500,8 @@ check_valid_drop_target (GtkPlacesSidebar *sidebar,
   GFile *dest_file;
   gint drag_action;
 
+  g_return_val_if_fail (value != NULL, TRUE);
+
   if (row == NULL)
     return FALSE;
 
@@ -1560,19 +1540,18 @@ check_valid_drop_target (GtkPlacesSidebar *sidebar,
     }
 
   /* Dragging a bookmark? */
-  if (sidebar->drag_data_received &&
-      sidebar->drag_data_info == DND_GTK_SIDEBAR_ROW)
+  if (G_VALUE_HOLDS (value, GTK_TYPE_SIDEBAR_ROW))
     {
       /* Don't allow reordering bookmarks into non-bookmark areas */
       valid = section_type == SECTION_BOOKMARKS;
     }
-  else
+  else if (G_VALUE_HOLDS (value, GDK_TYPE_FILE_LIST))
     {
       /* Dragging a file */
       if (uri != NULL)
         {
           dest_file = g_file_new_for_uri (uri);
-          drag_action = emit_drag_action_requested (sidebar, dest_file, sidebar->drag_list);
+          drag_action = emit_drag_action_requested (sidebar, dest_file, g_value_get_boxed (value));
           valid = drag_action > 0;
 
           g_object_unref (dest_file);
@@ -1582,6 +1561,11 @@ check_valid_drop_target (GtkPlacesSidebar *sidebar,
           valid = FALSE;
         }
     }
+  else
+    {
+      g_assert_not_reached ();
+      valid = TRUE;
+    }
 
   g_free (uri);
   return valid;
@@ -1589,7 +1573,7 @@ check_valid_drop_target (GtkPlacesSidebar *sidebar,
 
 static void
 update_possible_drop_targets (GtkPlacesSidebar *sidebar,
-                              gboolean          dragging)
+                              const GValue     *value)
 {
   GList *rows;
   GList *l;
@@ -1599,55 +1583,18 @@ update_possible_drop_targets (GtkPlacesSidebar *sidebar,
 
   for (l = rows; l != NULL; l = l->next)
     {
-      sensitive = !dragging || check_valid_drop_target (sidebar, GTK_SIDEBAR_ROW (l->data));
+      sensitive = value == NULL || check_valid_drop_target (sidebar, GTK_SIDEBAR_ROW (l->data), value);
       gtk_widget_set_sensitive (GTK_WIDGET (l->data), sensitive);
     }
 
   g_list_free (rows);
 }
 
-static void drag_data_received_callback (GObject      *source,
-                                         GAsyncResult *result,
-                                         gpointer      user_data);
-
-static gboolean
-get_drag_data (GtkPlacesSidebar *self,
-               GtkDropTarget    *dest,
-               GdkDrop          *drop,
-               GtkListBoxRow    *row)
-{
-  GdkContentFormats *formats = gdk_drop_get_formats (drop);
-
-  if (row)
-    g_object_set_data_full (G_OBJECT (drop), "places-sidebar-row", g_object_ref (row), g_object_unref);
-
-  gdk_drop_read_value_async (drop,
-                             gdk_content_formats_match_gtype (formats, formats),
-                             G_PRIORITY_DEFAULT,
-                             NULL,
-                             drag_data_received_callback,
-                             self->list_box);
-
-  return TRUE;
-}
-
 static void
-free_drag_data (GtkPlacesSidebar *sidebar)
+start_drop_feedback (GtkPlacesSidebar *sidebar,
+                     const GValue     *value)
 {
-  sidebar->drag_data_received = FALSE;
-
-  if (sidebar->drag_list)
-    {
-      g_slist_free_full (sidebar->drag_list, g_object_unref);
-      sidebar->drag_list = NULL;
-    }
-}
-
-static void
-start_drop_feedback (GtkPlacesSidebar *sidebar)
-{
-  if (sidebar->drag_data_received &&
-      sidebar->drag_data_info != DND_GTK_SIDEBAR_ROW)
+  if (value && !G_VALUE_HOLDS (value, GTK_TYPE_SIDEBAR_ROW))
     {
       gtk_sidebar_row_reveal (GTK_SIDEBAR_ROW (sidebar->new_bookmark_row));
       /* If the state is permanent, don't change it. The application controls it. */
@@ -1655,15 +1602,13 @@ start_drop_feedback (GtkPlacesSidebar *sidebar)
         sidebar->drop_state = DROP_STATE_NEW_BOOKMARK_ARMED;
     }
 
-  update_possible_drop_targets (sidebar, TRUE);
+  update_possible_drop_targets (sidebar, value);
 }
 
 static void
 stop_drop_feedback (GtkPlacesSidebar *sidebar)
 {
-  update_possible_drop_targets (sidebar, FALSE);
-
-  free_drag_data (sidebar);
+  update_possible_drop_targets (sidebar, NULL);
 
   if (sidebar->drop_state != DROP_STATE_NEW_BOOKMARK_ARMED_PERMANENT &&
       sidebar->new_bookmark_row != NULL)
@@ -1686,7 +1631,6 @@ stop_drop_feedback (GtkPlacesSidebar *sidebar)
     }
 
   sidebar->dragging_over = FALSE;
-  sidebar->drag_data_info = DND_UNKNOWN;
 }
 
 static GtkWidget *
@@ -1695,20 +1639,19 @@ create_placeholder_row (GtkPlacesSidebar *sidebar)
   return g_object_new (GTK_TYPE_SIDEBAR_ROW, "placeholder", TRUE, NULL);
 }
 
-static void
-drag_motion_callback (GtkDropTarget *dest,
-                      GdkDrop       *drop,
-                      gint           x,
-                      gint           y,
-                      gpointer       user_data)
+static GdkDragAction
+drag_motion_callback (GtkDropTarget    *target,
+                      double            x,
+                      double            y,
+                      GtkPlacesSidebar *sidebar)
 {
-  GtkPlacesSidebar *sidebar = GTK_PLACES_SIDEBAR (user_data);
-  gint action;
+  GdkDragAction action;
   GtkListBoxRow *row;
   GtkPlacesSidebarPlaceType place_type;
   gchar *drop_target_uri = NULL;
   gint row_index;
   gint row_placeholder_index;
+  const GValue *value;
 
   sidebar->dragging_over = TRUE;
   action = 0;
@@ -1716,17 +1659,16 @@ drag_motion_callback (GtkDropTarget *dest,
 
   gtk_list_box_drag_unhighlight_row (GTK_LIST_BOX (sidebar->list_box));
 
-  /* Nothing to do if no drag data */
-  if (!sidebar->drag_data_received &&
-      !get_drag_data (sidebar, dest, drop, row))
+  /* Nothing to do if no value yet */
+  value = gtk_drop_target_get_value (target);
+  if (value == NULL)
     goto out;
 
   /* Nothing to do if the target is not valid drop destination */
-  if (!check_valid_drop_target (sidebar, GTK_SIDEBAR_ROW (row)))
+  if (!check_valid_drop_target (sidebar, GTK_SIDEBAR_ROW (row), value))
     goto out;
 
-  if (sidebar->drag_data_received &&
-      sidebar->drag_data_info == DND_GTK_SIDEBAR_ROW)
+  if (G_VALUE_HOLDS (value, GTK_TYPE_SIDEBAR_ROW))
     {
       /* Dragging bookmarks always moves them to another position in the bookmarks list */
       action = GDK_ACTION_MOVE;
@@ -1783,7 +1725,7 @@ drag_motion_callback (GtkDropTarget *dest,
       gtk_list_box_prepend (GTK_LIST_BOX (sidebar->list_box),
                             sidebar->row_placeholder);
     }
-  else
+  else if (G_VALUE_HOLDS (value, GDK_TYPE_FILE_LIST))
     {
       gtk_list_box_drag_highlight_row (GTK_LIST_BOX (sidebar->list_box), row);
 
@@ -1795,32 +1737,33 @@ drag_motion_callback (GtkDropTarget *dest,
        * file move/copy operation itself, or if we should only try to
        * create bookmarks out of the dragged URIs.
        */
-      if (sidebar->drag_list != NULL)
+      if (place_type == PLACES_DROP_FEEDBACK)
         {
-          if (place_type == PLACES_DROP_FEEDBACK)
-            {
-              action = GDK_ACTION_COPY;
-            }
-          else
+          action = GDK_ACTION_COPY;
+        }
+      else
+        {
+          /* uri may be NULL for unmounted volumes, for example, so we don't allow drops there */
+          if (drop_target_uri != NULL)
             {
-              /* uri may be NULL for unmounted volumes, for example, so we don't allow drops there */
-              if (drop_target_uri != NULL)
-                {
-                  GFile *dest_file = g_file_new_for_uri (drop_target_uri);
+              GFile *dest_file = g_file_new_for_uri (drop_target_uri);
 
-                  action = emit_drag_action_requested (sidebar, dest_file, sidebar->drag_list);
+              action = emit_drag_action_requested (sidebar, dest_file, g_value_get_boxed (value));
 
-                  g_object_unref (dest_file);
-                }
+              g_object_unref (dest_file);
             }
         }
 
       g_free (drop_target_uri);
     }
+  else
+    {
+      g_assert_not_reached ();
+    }
 
  out:
-  start_drop_feedback (sidebar);
-  gdk_drop_status (drop, action);
+  start_drop_feedback (sidebar, value);
+  return action;
 }
 
 /* Reorders the bookmark to the specified position */
@@ -1867,51 +1810,26 @@ drop_files_as_bookmarks (GtkPlacesSidebar *sidebar,
     }
 }
 
-static void
-drag_data_received_callback (GObject      *source,
-                             GAsyncResult *result,
-                             gpointer      user_data)
+static gboolean
+drag_drop_callback (GtkDropTarget    *target,
+                    const GValue     *value,
+                    double            x,
+                    double            y,
+                    GtkPlacesSidebar *sidebar)
 {
-  GdkDrop *drop = GDK_DROP (source);
-  GtkWidget *list_box = user_data;
-  GtkPlacesSidebar *sidebar = GTK_PLACES_SIDEBAR (gtk_widget_get_ancestor (list_box, 
GTK_TYPE_PLACES_SIDEBAR));
   gint target_order_index;
   GtkPlacesSidebarPlaceType target_place_type;
   GtkPlacesSidebarSectionType target_section_type;
   gchar *target_uri;
   GtkListBoxRow *target_row;
-  GdkDragAction real_action;
-  const GValue *value;
-
-  value = gdk_drop_read_value_finish (drop, result, NULL);
-
-  if (!sidebar->drag_data_received)
-    {
-      if (G_VALUE_HOLDS (value, GDK_TYPE_FILE_LIST))
-        {
-          /* Free spurious drag data from previous drags if present */
-          if (sidebar->drag_list != NULL)
-            g_slist_free_full (sidebar->drag_list, g_object_unref);
-          sidebar->drag_list = g_slist_copy_deep (g_value_get_boxed (value), (GCopyFunc) g_object_ref, NULL);
-          sidebar->drag_data_info = DND_TEXT_URI_LIST;
-        }
-      else if (G_VALUE_HOLDS (value, GTK_TYPE_SIDEBAR_ROW))
-        {
-          sidebar->drag_list = NULL;
-          sidebar->drag_data_info = DND_GTK_SIDEBAR_ROW;
-        }
-      sidebar->drag_data_received = TRUE;
-    }
+  gboolean result;
 
-  if (!sidebar->drop_occurred)
-    return;
-
-  target_row = g_object_get_data (G_OBJECT (drop), "places-sidebar-row");
+  target_row = gtk_list_box_get_row_at_y (GTK_LIST_BOX (sidebar->list_box), y);
   if (target_row == NULL)
-    return;
+    return FALSE;
 
-  if (!check_valid_drop_target (sidebar, GTK_SIDEBAR_ROW (target_row)))
-    return;
+  if (!check_valid_drop_target (sidebar, GTK_SIDEBAR_ROW (target_row), value))
+    return FALSE;
 
   g_object_get (target_row,
                 "place-type", &target_place_type,
@@ -1919,9 +1837,9 @@ drag_data_received_callback (GObject      *source,
                 "order-index", &target_order_index,
                 "uri", &target_uri,
                 NULL);
-  real_action = 0;
+  result = FALSE;
 
-  if (sidebar->drag_data_info == DND_GTK_SIDEBAR_ROW)
+  if (G_VALUE_HOLDS (value, GTK_TYPE_SIDEBAR_ROW))
     {
       GtkWidget **source_row;
       /* A bookmark got reordered */
@@ -1934,41 +1852,37 @@ drag_data_received_callback (GObject      *source,
         g_object_get (sidebar->row_placeholder, "order-index", &target_order_index, NULL);
 
       reorder_bookmarks (sidebar, GTK_SIDEBAR_ROW (*source_row), target_order_index);
-      real_action = GDK_ACTION_MOVE;
+      result = TRUE;
     }
-  else
+  else if (G_VALUE_HOLDS (value, GDK_TYPE_FILE_LIST))
     {
       /* Dropping URIs! */
-
-      /* file transfer requested */
-      real_action = gdk_drop_get_actions (drop);
-
-      if (!gdk_drag_action_is_unique (real_action))
-        real_action = emit_drag_action_ask (sidebar, real_action);
-
-      if (real_action > 0)
+      if (target_place_type == PLACES_DROP_FEEDBACK)
         {
-          if (target_place_type == PLACES_DROP_FEEDBACK)
-            {
-              drop_files_as_bookmarks (sidebar, sidebar->drag_list, target_order_index);
-            }
-          else
-            {
-              GFile *dest_file = g_file_new_for_uri (target_uri);
-
-              emit_drag_perform_drop (sidebar, dest_file, sidebar->drag_list, real_action);
+          drop_files_as_bookmarks (sidebar, g_value_get_boxed (value), target_order_index);
+        }
+      else
+        {
+          GFile *dest_file = g_file_new_for_uri (target_uri);
+          
+          emit_drag_perform_drop (sidebar,
+                                  dest_file,
+                                  g_value_get_boxed (value),
+                                  gdk_drop_get_actions (gtk_drop_target_get_drop (target)));
 
-              g_object_unref (dest_file);
-            }
+          g_object_unref (dest_file);
         }
+      result = TRUE;
+    }
+  else
+    {
+      g_assert_not_reached ();
     }
 
 out:
-  sidebar->drop_occurred = FALSE;
-  g_object_set_data (G_OBJECT (drop), "places-sidebar-row", NULL);
-  gdk_drop_finish (drop, real_action);
   stop_drop_feedback (sidebar);
   g_free (target_uri);
+  return result;
 }
 
 static void
@@ -2010,28 +1924,7 @@ drag_leave_callback (GtkDropTarget *dest,
       sidebar->drop_state = DROP_STATE_NORMAL;
     }
 
-  sidebar->drag_data_received = FALSE;
   sidebar->dragging_over = FALSE;
-  sidebar->drag_data_info = DND_UNKNOWN;
-}
-
-static gboolean
-drag_drop_callback (GtkDropTarget *dest,
-                    GdkDrop       *drop,
-                    int            x,
-                    int            y,
-                    gpointer       user_data)
-{
-  GtkPlacesSidebar *sidebar = GTK_PLACES_SIDEBAR (user_data);
-  gboolean retval = FALSE;
-  GtkListBoxRow *row;
-
-  row = gtk_list_box_get_row_at_y (GTK_LIST_BOX (sidebar->list_box), y);
-  sidebar->drop_occurred = TRUE;
- 
-  retval = get_drag_data (sidebar, dest, drop, row);
-
-  return retval;
 }
 
 static void
@@ -3842,11 +3735,10 @@ shell_shows_desktop_changed (GtkSettings *settings,
 static void
 gtk_places_sidebar_init (GtkPlacesSidebar *sidebar)
 {
-  GtkDropTarget *dest;
+  GtkDropTarget *target;
   gboolean show_desktop;
   GtkEventController *controller;
   GtkGesture *gesture;
-  GdkContentFormatsBuilder *builder;
 
   sidebar->cancellable = g_cancellable_new ();
 
@@ -3901,20 +3793,18 @@ gtk_places_sidebar_init (GtkPlacesSidebar *sidebar)
   gtk_widget_add_controller (GTK_WIDGET (sidebar), GTK_EVENT_CONTROLLER (gesture));
 
   /* DND support */
-  builder = gdk_content_formats_builder_new ();
-  gdk_content_formats_builder_add_gtype (builder, GTK_TYPE_SIDEBAR_ROW);
-  gdk_content_formats_builder_add_gtype (builder, GDK_TYPE_FILE_LIST);
-  dest = gtk_drop_target_new (gdk_content_formats_builder_free_to_formats (builder),
-                              GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK);
-  g_signal_connect (dest, "drag-motion", G_CALLBACK (drag_motion_callback), sidebar);
-  g_signal_connect (dest, "drag-drop", G_CALLBACK (drag_drop_callback), sidebar);
-  g_signal_connect (dest, "drag-leave", G_CALLBACK (drag_leave_callback), sidebar);
-  gtk_widget_add_controller (sidebar->list_box, GTK_EVENT_CONTROLLER (dest));
+  target = gtk_drop_target_new (G_TYPE_INVALID, GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK);
+  gtk_drop_target_set_preload (target, TRUE);
+  gtk_drop_target_set_gtypes (target, (GType[2]) { GTK_TYPE_SIDEBAR_ROW, GDK_TYPE_FILE_LIST }, 2);
+  g_signal_connect (target, "enter", G_CALLBACK (drag_motion_callback), sidebar);
+  g_signal_connect (target, "motion", G_CALLBACK (drag_motion_callback), sidebar);
+  g_signal_connect (target, "drop", G_CALLBACK (drag_drop_callback), sidebar);
+  g_signal_connect (target, "leave", G_CALLBACK (drag_leave_callback), sidebar);
+  gtk_widget_add_controller (sidebar->list_box, GTK_EVENT_CONTROLLER (target));
 
   sidebar->drag_row = NULL;
   sidebar->row_placeholder = NULL;
   sidebar->dragging_over = FALSE;
-  sidebar->drag_data_info = DND_UNKNOWN;
 
   gtk_container_add (GTK_CONTAINER (sidebar->swin), sidebar->list_box);
 
@@ -4071,8 +3961,6 @@ gtk_places_sidebar_dispose (GObject *object)
       sidebar->cancellable = NULL;
     }
 
-  free_drag_data (sidebar);
-
   if (sidebar->bookmarks_manager != NULL)
     {
       _gtk_bookmarks_manager_free (sidebar->bookmarks_manager);
@@ -5068,7 +4956,7 @@ gtk_places_sidebar_set_drop_targets_visible (GtkPlacesSidebar *sidebar,
   if (visible)
     {
       sidebar->drop_state = DROP_STATE_NEW_BOOKMARK_ARMED_PERMANENT;
-      start_drop_feedback (sidebar);
+      start_drop_feedback (sidebar, NULL);
     }
   else
     {
diff --git a/gtk/gtktext.c b/gtk/gtktext.c
index 56f748d312..52af78a211 100644
--- a/gtk/gtktext.c
+++ b/gtk/gtktext.c
@@ -30,6 +30,9 @@
 #include "gtkbutton.h"
 #include "gtkcssnodeprivate.h"
 #include "gtkdebug.h"
+#include "gtkdragicon.h"
+#include "gtkdragsource.h"
+#include "gtkdroptarget.h"
 #include "gtkeditable.h"
 #include "gtkemojichooser.h"
 #include "gtkemojicompletion.h"
@@ -65,9 +68,6 @@
 #include "gtkwindow.h"
 #include "gtknative.h"
 #include "gtkactionmuxerprivate.h"
-#include "gtkdragsource.h"
-#include "gtkdragdest.h"
-#include "gtkdragicon.h"
 
 #include "a11y/gtktextaccessible.h"
 
@@ -190,7 +190,6 @@ struct _GtkTextPrivate
   int           dnd_position;               /* In chars, -1 == no DND cursor */
   int           drag_start_x;
   int           drag_start_y;
-  int           drop_position;              /* where the drop should happen */
   int           insert_pos;
   int           selection_bound;
   int           scroll_offset;
@@ -336,20 +335,18 @@ static void   gtk_text_state_flags_changed  (GtkWidget        *widget,
 static void   gtk_text_root                 (GtkWidget        *widget);
 
 static gboolean gtk_text_drag_drop          (GtkDropTarget    *dest,
-                                             GdkDrop          *drop,
-                                             int               x,
-                                             int               y,
+                                             const GValue     *value,
+                                             double            x,
+                                             double            y,
                                              GtkText          *text);
 static gboolean gtk_text_drag_accept        (GtkDropTarget    *dest,
                                              GdkDrop          *drop,
                                              GtkText          *self);
-static void     gtk_text_drag_motion        (GtkDropTarget    *dest,
-                                             GdkDrop          *drop,
-                                             int               x,
-                                             int               y,
+static GdkDragAction gtk_text_drag_motion   (GtkDropTarget    *dest,
+                                             double            x,
+                                             double            y,
                                              GtkText          *text);
 static void     gtk_text_drag_leave         (GtkDropTarget    *dest,
-                                             GdkDrop          *drop,
                                              GtkText          *text);
 
 
@@ -1712,7 +1709,7 @@ gtk_text_init (GtkText *self)
   GtkGesture *gesture;
   GtkEventController *controller;
   int i;
-  GtkDropTarget *dest;
+  GtkDropTarget *target;
 
   gtk_widget_set_can_focus (GTK_WIDGET (self), TRUE);
   gtk_widget_set_overflow (GTK_WIDGET (self), GTK_OVERFLOW_HIDDEN);
@@ -1734,13 +1731,13 @@ gtk_text_init (GtkText *self)
   priv->selection_content = g_object_new (GTK_TYPE_TEXT_CONTENT, NULL);
   GTK_TEXT_CONTENT (priv->selection_content)->self = self;
 
-  dest = gtk_drop_target_new (gdk_content_formats_new_for_gtype (G_TYPE_STRING),
-                              GDK_ACTION_COPY | GDK_ACTION_MOVE);
-  g_signal_connect (dest, "accept", G_CALLBACK (gtk_text_drag_accept), self);
-  g_signal_connect (dest, "drag-motion", G_CALLBACK (gtk_text_drag_motion), self);
-  g_signal_connect (dest, "drag-leave", G_CALLBACK (gtk_text_drag_leave), self);
-  g_signal_connect (dest, "drag-drop", G_CALLBACK (gtk_text_drag_drop), self);
-  gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (dest));
+  target = gtk_drop_target_new (G_TYPE_STRING, GDK_ACTION_COPY | GDK_ACTION_MOVE);
+  g_signal_connect (target, "accept", G_CALLBACK (gtk_text_drag_accept), self);
+  g_signal_connect (target, "enter", G_CALLBACK (gtk_text_drag_motion), self);
+  g_signal_connect (target, "motion", G_CALLBACK (gtk_text_drag_motion), self);
+  g_signal_connect (target, "leave", G_CALLBACK (gtk_text_drag_leave), self);
+  g_signal_connect (target, "drop", G_CALLBACK (gtk_text_drag_drop), self);
+  gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (target));
 
   /* This object is completely private. No external entity can gain a reference
    * to it; so we create it here and destroy it in finalize().
@@ -6120,8 +6117,7 @@ gtk_text_selection_bubble_popup_set (GtkText *self)
 
 static void
 gtk_text_drag_leave (GtkDropTarget *dest,
-                     GdkDrop       *drop,
-                     GtkText *self)
+                     GtkText       *self)
 {
   GtkTextPrivate *priv = gtk_text_get_instance_private (self);
   GtkWidget *widget = GTK_WIDGET (self);
@@ -6130,94 +6126,45 @@ gtk_text_drag_leave (GtkDropTarget *dest,
   gtk_widget_queue_draw (widget);
 }
 
-static GdkDragAction
-gtk_text_get_action (GtkText *self,
-                     GdkDrop *drop)
+static gboolean
+gtk_text_drag_drop (GtkDropTarget *dest,
+                    const GValue  *value,
+                    double         x,
+                    double         y,
+                    GtkText       *self)
 {
   GtkTextPrivate *priv = gtk_text_get_instance_private (self);
-  GdkDrag *drag = gdk_drop_get_drag (drop);
-  GdkDragAction actions;
-
-  actions = gdk_drop_get_actions (drop);
-
-  if (drag == priv->drag &&
-      actions & GDK_ACTION_MOVE)
-    return GDK_ACTION_MOVE;
-
-  if (actions & GDK_ACTION_COPY)
-    return GDK_ACTION_COPY;
-
-  if (actions & GDK_ACTION_MOVE)
-    return GDK_ACTION_MOVE;
+  int drop_position;
+  int length;
+  const char *str;
 
-  return 0;
-}
+  if (!priv->editable)
+    return FALSE;
 
-static void
-got_text (GObject      *source,
-          GAsyncResult *result,
-          gpointer      data)
-{
-  GdkDrop *drop = GDK_DROP (source);
-  GtkText *self = GTK_TEXT (data);
-  GtkTextPrivate *priv = gtk_text_get_instance_private (self);
-  char *str;
-  GdkDragAction action;
+  drop_position = gtk_text_find_position (self, x + priv->scroll_offset);
 
-  str = gdk_drop_read_text_finish (drop, result, NULL);
-  action = gtk_text_get_action (self, drop);
+  str = g_value_get_string (value);
+  if (priv->truncate_multiline)
+    length = truncate_multiline (str);
+  else
+    length = -1;
 
-  if (action && str)
+  if (priv->selection_bound == priv->current_pos ||
+      drop_position < priv->selection_bound ||
+      drop_position > priv->current_pos)
     {
-      int length = -1;
-      int pos;
-
-      if (priv->truncate_multiline)
-        length = truncate_multiline (str);
-
-      if (priv->selection_bound == priv->current_pos ||
-          priv->drop_position < priv->selection_bound ||
-          priv->drop_position > priv->current_pos)
-        {
-          gtk_text_insert_text (self, str, length, &priv->drop_position);
-        }
-      else
-        {
-          /* Replacing selection */
-          begin_change (self);
-          gtk_text_delete_selection (self);
-          pos = MIN (priv->selection_bound, priv->current_pos);
-          gtk_text_insert_text (self, str, length, &pos);
-          end_change (self);
-        }
-      
-      gdk_drop_finish (drop, action);
+      gtk_text_insert_text (self, str, length, &drop_position);
     }
   else
     {
-      /* Drag and drop didn't happen! */
-      gdk_drop_finish (drop, 0);
-    }
-
-  g_free (str);
-}
-
-static gboolean
-gtk_text_drag_drop (GtkDropTarget *dest,
-                    GdkDrop       *drop,
-                    int            x,
-                    int            y,
-                    GtkText       *self)
-{
-  GtkTextPrivate *priv = gtk_text_get_instance_private (self);
-
-  if (priv->editable && gdk_drop_has_value (drop, G_TYPE_STRING))
-    {
-      priv->drop_position = gtk_text_find_position (self, x + priv->scroll_offset);
-      gdk_drop_read_text_async (drop, NULL, got_text, self);
+      int pos;
+      /* Replacing selection */
+      begin_change (self);
+      gtk_text_delete_selection (self);
+      pos = MIN (priv->selection_bound, priv->current_pos);
+      gtk_text_insert_text (self, str, length, &pos);
+      end_change (self);
     }
-  else
-    gdk_drop_finish (drop, 0);
   
   return TRUE;
 }
@@ -6238,40 +6185,42 @@ gtk_text_drag_accept (GtkDropTarget *dest,
   return gdk_content_formats_match (gtk_drop_target_get_formats (dest), gdk_drop_get_formats (drop));
 }
 
-static void
-gtk_text_drag_motion (GtkDropTarget *dest,
-                      GdkDrop       *drop,
-                      int            x,
-                      int            y,
-                      GtkText        *self)
+static GdkDragAction
+gtk_text_drag_motion (GtkDropTarget *target,
+                      double         x,
+                      double         y,
+                      GtkText       *self)
 {
   GtkTextPrivate *priv = gtk_text_get_instance_private (self);
   int new_position, old_position;
 
+  if (!priv->editable)
+    {
+      gtk_drop_target_reject (target);
+      return 0;
+    }
+
   old_position = priv->dnd_position;
   new_position = gtk_text_find_position (self, x + priv->scroll_offset);
 
-  if (priv->editable)
+  if (priv->selection_bound == priv->current_pos ||
+      new_position < priv->selection_bound ||
+      new_position > priv->current_pos)
     {
-      if (priv->selection_bound == priv->current_pos ||
-          new_position < priv->selection_bound ||
-          new_position > priv->current_pos)
-        {
-          priv->dnd_position = new_position;
-        }
-      else
-        {
-          priv->dnd_position = -1;
-        }
+      priv->dnd_position = new_position;
     }
   else
     {
-      /* Entry not editable, or no text */
       priv->dnd_position = -1;
     }
 
   if (priv->dnd_position != old_position)
     gtk_widget_queue_draw (GTK_WIDGET (self));
+
+  if (priv->drag)
+    return GDK_ACTION_MOVE;
+  else
+    return GDK_ACTION_COPY;
 }
 
 /* We display the cursor when
diff --git a/gtk/gtktextview.c b/gtk/gtktextview.c
index a6cb66ceb5..032bc620e5 100644
--- a/gtk/gtktextview.c
+++ b/gtk/gtktextview.c
@@ -423,17 +423,16 @@ static GtkTextBuffer* gtk_text_view_create_buffer (GtkTextView   *text_view);
 
 /* Target side drag signals */
 static void     gtk_text_view_drag_leave         (GtkDropTarget    *dest,
-                                                  GdkDrop          *drop,
                                                   GtkTextView      *text_view);
-static gboolean gtk_text_view_drag_motion        (GtkDropTarget    *dest,
-                                                  GdkDrop          *drop,
-                                                  int               x,
-                                                  int               y,
+static GdkDragAction
+                gtk_text_view_drag_motion        (GtkDropTarget    *dest,
+                                                  double            x,
+                                                  double            y,
                                                   GtkTextView      *text_view);
 static gboolean gtk_text_view_drag_drop          (GtkDropTarget    *dest,
-                                                  GdkDrop          *drop,
-                                                  int               x,
-                                                  int               y,
+                                                  const GValue     *value,
+                                                  double            x,
+                                                  double            y,
                                                   GtkTextView      *text_view);
 
 static gboolean gtk_text_view_popup_menu         (GtkWidget     *widget);
@@ -1752,10 +1751,11 @@ gtk_text_view_init (GtkTextView *text_view)
 
   priv->scroll_after_paste = FALSE;
 
-  dest = gtk_drop_target_new (gdk_content_formats_new_for_gtype (GTK_TYPE_TEXT_BUFFER), GDK_ACTION_COPY | 
GDK_ACTION_MOVE);
-  g_signal_connect (dest, "drag-leave", G_CALLBACK (gtk_text_view_drag_leave), text_view);
-  g_signal_connect (dest, "drag-motion", G_CALLBACK (gtk_text_view_drag_motion), text_view);
-  g_signal_connect (dest, "drag-drop", G_CALLBACK (gtk_text_view_drag_drop), text_view);
+  dest = gtk_drop_target_new (G_TYPE_STRING, GDK_ACTION_COPY | GDK_ACTION_MOVE);
+  g_signal_connect (dest, "enter", G_CALLBACK (gtk_text_view_drag_motion), text_view);
+  g_signal_connect (dest, "motion", G_CALLBACK (gtk_text_view_drag_motion), text_view);
+  g_signal_connect (dest, "leave", G_CALLBACK (gtk_text_view_drag_leave), text_view);
+  g_signal_connect (dest, "drop", G_CALLBACK (gtk_text_view_drag_drop), text_view);
   gtk_widget_add_controller (GTK_WIDGET (text_view), GTK_EVENT_CONTROLLER (dest));
 
   controller = gtk_drop_controller_motion_new ();
@@ -7788,7 +7788,6 @@ gtk_text_view_start_selection_dnd (GtkTextView       *text_view,
 
 static void
 gtk_text_view_drag_leave (GtkDropTarget *dest,
-                          GdkDrop       *drop,
                           GtkTextView   *text_view)
 {
   GtkTextViewPrivate *priv = text_view->priv;
@@ -7796,11 +7795,10 @@ gtk_text_view_drag_leave (GtkDropTarget *dest,
   gtk_text_mark_set_visible (priv->dnd_mark, FALSE);
 }
 
-static gboolean
+static GdkDragAction
 gtk_text_view_drag_motion (GtkDropTarget *dest,
-                           GdkDrop       *drop,
-                           int            x,
-                           int            y,
+                           double         x,
+                           double         y,
                            GtkTextView   *text_view)
 {
   GtkTextViewPrivate *priv = text_view->priv;
@@ -7834,117 +7832,50 @@ gtk_text_view_drag_motion (GtkDropTarget *dest,
   if (can_accept)
     {
       gtk_text_mark_set_visible (priv->dnd_mark, cursor_visible (text_view));
-      gdk_drop_status (drop, GDK_ACTION_COPY | GDK_ACTION_MOVE);
+      if (text_view->priv->drag)
+        return GDK_ACTION_MOVE;
+      else
+        return GDK_ACTION_COPY;
     }
   else
     {
-      gdk_drop_status (drop, 0);
       gtk_text_mark_set_visible (priv->dnd_mark, FALSE);
+      return 0;
     }
-
-  /* TRUE return means don't propagate the drag motion to parent
-   * widgets that may also be drop sites.
-   */
-  return TRUE;
 }
 
-static GdkDragAction
-gtk_text_view_get_action (GtkTextView *textview,
-                          GdkDrop     *drop)
-{
-  GdkDrag *drag = gdk_drop_get_drag (drop);
-  GdkDragAction actions;
-
-  actions = gdk_drop_get_actions (drop);
-
-  if (drag == textview->priv->drag &&
-      actions & GDK_ACTION_MOVE)
-    return GDK_ACTION_MOVE;
-
-  if (actions & GDK_ACTION_COPY)
-    return GDK_ACTION_COPY;
-
-  if (actions & GDK_ACTION_MOVE)
-    return GDK_ACTION_MOVE;
-
-  return 0;
-}
-
-static void
-got_text (GObject *source,
-          GAsyncResult *result,
-          gpointer data)
+static gboolean
+gtk_text_view_drag_drop (GtkDropTarget *dest,
+                         const GValue  *value,
+                         double         x,
+                         double         y,
+                         GtkTextView   *text_view)
 {
-  GdkDrop *drop = GDK_DROP (source);
-  GtkTextView *text_view = GTK_TEXT_VIEW (data);
   GtkTextViewPrivate *priv = text_view->priv;
   GtkTextBuffer *buffer;
-  char *str;
   GtkTextIter drop_point;
-  GdkDragAction action;
-
-  str = gdk_drop_read_text_finish (drop, result, NULL);
-  if (!str)
-    {
-      gdk_drop_finish (drop, 0);
-      return;
-    }
 
   buffer = get_buffer (text_view);
   gtk_text_buffer_get_iter_at_mark (buffer, &drop_point, priv->dnd_mark);
 
-  action = gtk_text_view_get_action (text_view, drop);
+  if (!gtk_text_iter_can_insert (&drop_point, priv->editable))
+    return FALSE;
 
   gtk_text_buffer_begin_user_action (buffer);
 
   if (!gtk_text_buffer_insert_interactive (buffer,
-                                           &drop_point, (gchar *) str, -1,
+                                           &drop_point, (gchar *) g_value_get_string (value), -1,
                                            text_view->priv->editable))
     gtk_widget_error_bell (GTK_WIDGET (text_view));
 
-  g_free (str);
-
   gtk_text_buffer_get_iter_at_mark (buffer, &drop_point, priv->dnd_mark);
   gtk_text_buffer_place_cursor (buffer, &drop_point);
 
   gtk_text_buffer_end_user_action (buffer);
 
-  gdk_drop_finish (drop, action);
-}
-
-static gboolean
-gtk_text_view_drag_drop (GtkDropTarget *dest,
-                         GdkDrop       *drop,
-                         int            x,
-                         int            y,
-                         GtkTextView *text_view)
-{
-  GtkTextViewPrivate *priv = text_view->priv;
-  GtkTextIter drop_point;
-  GtkTextBuffer *buffer = NULL;
-
-  gtk_text_mark_set_visible (priv->dnd_mark, FALSE);
-
-  buffer = get_buffer (text_view);
-  gtk_text_buffer_get_iter_at_mark (buffer, &drop_point, priv->dnd_mark);
-  
-  if (!gtk_text_iter_can_insert (&drop_point, priv->editable))
-    goto done;
-
-  if (gtk_text_view_get_action (text_view, drop) == 0)
-    goto done;
-
-  if (gdk_drop_has_value (drop, G_TYPE_STRING))
-    {
-       gdk_drop_read_text_async (drop, NULL, got_text, text_view);
-       return TRUE;
-    }
-
-done:
-  gdk_drop_finish (drop, 0);
   return TRUE;
 }
-        
+
 static void
 gtk_text_view_set_hadjustment (GtkTextView   *text_view,
                                GtkAdjustment *adjustment)
diff --git a/gtk/gtktreeview.c b/gtk/gtktreeview.c
index e56c489d9b..a966c8ef0e 100644
--- a/gtk/gtktreeview.c
+++ b/gtk/gtktreeview.c
@@ -31,9 +31,9 @@
 #include "gtkcssnumbervalueprivate.h"
 #include "gtkcsscolorvalueprivate.h"
 #include "gtkcssstylepropertyprivate.h"
-#include "gtkdragdest.h"
 #include "gtkdragsource.h"
 #include "gtkdragicon.h"
+#include "gtkdroptargetasync.h"
 #include "gtkentryprivate.h"
 #include "gtksearchentryprivate.h"
 #include "gtkeventcontrollerkey.h"
@@ -311,7 +311,7 @@ struct _TreeViewDragInfo
   GtkTreeRowReference *source_item;
 
   GtkCssNode *cssnode;
-  GtkDropTarget *dest;
+  GtkDropTargetAsync *dest;
   GdkModifierType start_button_mask;
 
   guint source_set : 1;
@@ -667,8 +667,8 @@ static void     gtk_tree_view_key_controller_key_released (GtkEventControllerKey
                                                            guint                  keycode,
                                                            GdkModifierType        state,
                                                            GtkTreeView           *tree_view);
-static void     gtk_tree_view_focus_controller_focus_out  (GtkEventController   *focus,
-                                                           GtkTreeView            *tree_view);
+static void     gtk_tree_view_focus_controller_focus_out  (GtkEventController    *focus,
+                                                           GtkTreeView           *tree_view);
 
 static gint     gtk_tree_view_focus                (GtkWidget        *widget,
                                                    GtkDirectionType  direction);
@@ -690,22 +690,22 @@ static GdkContentProvider * gtk_tree_view_drag_data_get   (GtkTreeView
                                                            GtkTreePath           *source_row);
 
 /* Target side drag signals */
-static void     gtk_tree_view_drag_leave         (GtkDropTarget    *dest,
-                                                  GdkDrop          *drop,
-                                                  GtkTreeView      *tree_view);
-static void     gtk_tree_view_drag_motion        (GtkDropTarget    *dest,
-                                                  GdkDrop          *drop,
-                                                  int               x,
-                                                  int               y,
-                                                  GtkTreeView      *tree_view);
-static gboolean gtk_tree_view_drag_drop          (GtkDropTarget    *dest,
-                                                  GdkDrop          *drop,
-                                                  int               x,
-                                                  int               y,
-                                                  GtkTreeView      *tree_view);
-static void     gtk_tree_view_drag_data_received (GObject      *source,
-                                                  GAsyncResult *result,
-                                                  gpointer      data);
+static void     gtk_tree_view_drag_leave                  (GtkDropTargetAsync    *dest,
+                                                           GdkDrop               *drop,
+                                                           GtkTreeView           *tree_view);
+static GdkDragAction gtk_tree_view_drag_motion            (GtkDropTargetAsync    *dest,
+                                                           GdkDrop               *drop,
+                                                           double                 x,
+                                                           double                 y,
+                                                           GtkTreeView           *tree_view);
+static gboolean gtk_tree_view_drag_drop                   (GtkDropTargetAsync    *dest,
+                                                           GdkDrop               *drop,
+                                                           double                 x,
+                                                           double                 y,
+                                                           GtkTreeView           *tree_view);
+static void     gtk_tree_view_drag_data_received          (GObject               *source,
+                                                           GAsyncResult          *result,
+                                                           gpointer               data);
 
 /* tree_model signals */
 static gboolean gtk_tree_view_real_move_cursor            (GtkTreeView     *tree_view,
@@ -6859,13 +6859,13 @@ scroll_row_timeout (gpointer data)
 
 /* Returns TRUE if event should not be propagated to parent widgets */
 static gboolean
-set_destination_row (GtkTreeView    *tree_view,
-                     GtkDropTarget  *dest,
+set_destination_row (GtkTreeView         *tree_view,
+                     GtkDropTargetAsync  *dest,
                      /* coordinates relative to the widget */
-                     gint            x,
-                     gint            y,
-                     GdkDragAction  *suggested_action,
-                     GType          *target)
+                     gint                 x,
+                     gint                 y,
+                     GdkDragAction       *suggested_action,
+                     GType               *target)
 {
   GtkTreePath *path = NULL;
   GtkTreeViewDropPosition pos;
@@ -6899,7 +6899,7 @@ set_destination_row (GtkTreeView    *tree_view,
       return FALSE; /* no longer a drop site */
     }
 
-  formats = gtk_drop_target_get_formats (dest);
+  formats = gtk_drop_target_async_get_formats (dest);
   *target = gdk_content_formats_match_gtype (formats, formats);
   if (*target == G_TYPE_INVALID)
     return FALSE;
@@ -7195,9 +7195,9 @@ gtk_tree_view_drag_data_get (GtkTreeView *tree_view,
 }
 
 static void
-gtk_tree_view_drag_leave (GtkDropTarget *dest,
-                          GdkDrop       *drop,
-                          GtkTreeView   *tree_view)
+gtk_tree_view_drag_leave (GtkDropTargetAsync *dest,
+                          GdkDrop            *drop,
+                          GtkTreeView        *tree_view)
 {
   /* unset any highlight row */
   gtk_tree_view_set_drag_dest_row (tree_view,
@@ -7212,12 +7212,12 @@ gtk_tree_view_drag_leave (GtkDropTarget *dest,
 }
 
 
-static void
-gtk_tree_view_drag_motion (GtkDropTarget *dest,
-                           GdkDrop       *drop,
-                           int            x,
-                           int            y,
-                           GtkTreeView   *tree_view)
+static GdkDragAction
+gtk_tree_view_drag_motion (GtkDropTargetAsync *dest,
+                           GdkDrop            *drop,
+                           double              x,
+                           double              y,
+                           GtkTreeView        *tree_view)
 {
   gboolean empty;
   GtkTreePath *path = NULL;
@@ -7226,10 +7226,7 @@ gtk_tree_view_drag_motion (GtkDropTarget *dest,
   GType target;
 
   if (!set_destination_row (tree_view, dest, x, y, &suggested_action, &target))
-    {
-      gdk_drop_status (drop, 0);
-      return;
-    }
+    return 0;
 
   tree_view->event_last_x = x;
   tree_view->event_last_y = y;
@@ -7241,8 +7238,7 @@ gtk_tree_view_drag_motion (GtkDropTarget *dest,
 
   if (path == NULL && !empty)
     {
-      /* Can't drop here. */
-      gdk_drop_status (drop, 0);
+      suggested_action = 0;
     }
   else
     {
@@ -7270,21 +7266,22 @@ gtk_tree_view_drag_motion (GtkDropTarget *dest,
       else
         {
           set_status_pending (drop, 0);
-          gdk_drop_status (drop, suggested_action);
         }
     }
 
   if (path)
     gtk_tree_path_free (path);
+
+  return suggested_action;
 }
 
 
 static gboolean
-gtk_tree_view_drag_drop (GtkDropTarget *dest,
-                         GdkDrop       *drop,
-                         int            x,
-                         int            y,
-                         GtkTreeView   *tree_view)
+gtk_tree_view_drag_drop (GtkDropTargetAsync *dest,
+                         GdkDrop            *drop,
+                         double              x,
+                         double              y,
+                         GtkTreeView        *tree_view)
 {
   GtkTreePath *path;
   GdkDragAction suggested_action = 0;
@@ -7436,8 +7433,6 @@ gtk_tree_view_drag_data_received (GObject      *source,
             }
         }
 
-      gdk_drop_status (drop, suggested_action);
-
       if (path)
         gtk_tree_path_free (path);
 
@@ -12869,10 +12864,11 @@ gtk_tree_view_enable_model_drag_dest (GtkTreeView       *tree_view,
   di = ensure_info (tree_view);
   di->dest_set = TRUE;
 
-  di->dest = gtk_drop_target_new (gdk_content_formats_ref (formats), actions);
+  di->dest = gtk_drop_target_async_new (gdk_content_formats_ref (formats), actions);
   g_signal_connect (di->dest, "drag-leave", G_CALLBACK (gtk_tree_view_drag_leave), tree_view);
+  g_signal_connect (di->dest, "drag-enter", G_CALLBACK (gtk_tree_view_drag_motion), tree_view);
   g_signal_connect (di->dest, "drag-motion", G_CALLBACK (gtk_tree_view_drag_motion), tree_view);
-  g_signal_connect (di->dest, "drag-drop", G_CALLBACK (gtk_tree_view_drag_drop), tree_view);
+  g_signal_connect (di->dest, "drop", G_CALLBACK (gtk_tree_view_drag_drop), tree_view);
   gtk_widget_add_controller (GTK_WIDGET (tree_view), GTK_EVENT_CONTROLLER (di->dest));
   g_object_ref (di->dest);
 
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index 736bc8df15..0a28d8385d 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -5489,7 +5489,7 @@ gtk_widget_has_grab (GtkWidget *widget)
 {
   GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget);
 
-  g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
+  if (!GTK_IS_WIDGET (widget)) return FALSE;
 
   return priv->has_grab;
 }
diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c
index 58fd464912..2f6b536499 100644
--- a/gtk/gtkwindow.c
+++ b/gtk/gtkwindow.c
@@ -40,7 +40,7 @@
 #include "gtkcsscolorvalueprivate.h"
 #include "gtkcssshadowvalueprivate.h"
 #include "gtkcssstylepropertyprivate.h"
-#include "gtkdragdest.h"
+#include "gtkdroptargetasync.h"
 #include "gtkeventcontrollerfocus.h"
 #include "gtkeventcontrollerkey.h"
 #include "gtkeventcontrollermotion.h"
@@ -280,12 +280,6 @@ typedef struct
   GtkConstraintSolver *constraint_solver;
 } GtkWindowPrivate;
 
-#ifdef GDK_WINDOWING_X11
-static const char *dnd_dest_targets [] = {
-  "application/x-rootwindow-drop"
-};
-#endif
-
 enum {
   SET_FOCUS,
   ACTIVATE_FOCUS,
@@ -1767,6 +1761,18 @@ gtk_window_activate_default_activate (GtkWidget  *widget,
   gtk_window_real_activate_default (GTK_WINDOW (widget));
 }
 
+static gboolean
+gtk_window_accept_rootwindow_drop (GtkDropTargetAsync *self,
+                                   GdkDrop            *drop,
+                                   double              x,
+                                   double              y,
+                                   gpointer            unused)
+{
+  gdk_drop_finish (drop, GDK_ACTION_MOVE);
+
+  return TRUE;
+}
+
 static void
 gtk_window_init (GtkWindow *window)
 {
@@ -1776,9 +1782,7 @@ gtk_window_init (GtkWindow *window)
   GdkSeat *seat;
   GtkEventController *motion_controller;
   GtkEventController *controller;
-#ifdef GDK_WINDOWING_X11
-  GtkDropTarget *dest;
-#endif
+  GtkDropTargetAsync *target;
 
   widget = GTK_WIDGET (window);
 
@@ -1829,10 +1833,10 @@ gtk_window_init (GtkWindow *window)
 
   priv->scale = gtk_widget_get_scale_factor (widget);
 
-#ifdef GDK_WINDOWING_X11
-  dest = gtk_drop_target_new (gdk_content_formats_new (dnd_dest_targets, G_N_ELEMENTS (dnd_dest_targets)), 
GDK_ACTION_MOVE);
-  gtk_widget_add_controller (GTK_WIDGET (window), GTK_EVENT_CONTROLLER (dest));
-#endif
+  target = gtk_drop_target_async_new (gdk_content_formats_new ((const char*[1]) { 
"application/x-rootwindow-drop" }, 1),
+                                      GDK_ACTION_MOVE);
+  g_signal_connect (target, "drop", G_CALLBACK (gtk_window_accept_rootwindow_drop), NULL);
+  gtk_widget_add_controller (GTK_WIDGET (window), GTK_EVENT_CONTROLLER (target));
 
   seat = gdk_display_get_default_seat (gtk_widget_get_display (widget));
   g_signal_connect (seat, "device-removed",
diff --git a/gtk/meson.build b/gtk/meson.build
index ca72c02101..d68b54539c 100644
--- a/gtk/meson.build
+++ b/gtk/meson.build
@@ -98,6 +98,7 @@ gtk_private_sources = files([
   'gtkcssvalue.c',
   'gtkcsswidgetnode.c',
   'gtkcustomlayout.c',
+  'gtkdrop.c',
   'gtkfilechooserembed.c',
   'gtkfilechooserentry.c',
   'gtkfilechoosererrorstack.c',
@@ -207,11 +208,12 @@ gtk_public_sources = files([
   'gtkcontainer.c',
   'gtkcssprovider.c',
   'gtkdialog.c',
-  'gtkdragdest.c',
   'gtkdragicon.c',
   'gtkdragsource.c',
   'gtkdrawingarea.c',
   'gtkdropcontrollermotion.c',
+  'gtkdroptarget.c',
+  'gtkdroptargetasync.c',
   'gtkeditable.c',
   'gtkemojichooser.c',
   'gtkemojicompletion.c',
@@ -457,11 +459,12 @@ gtk_public_headers = files([
   'gtkcustomlayout.h',
   'gtkdebug.h',
   'gtkdialog.h',
-  'gtkdragdest.h',
   'gtkdragicon.h',
   'gtkdragsource.h',
   'gtkdrawingarea.h',
   'gtkdropcontrollermotion.h',
+  'gtkdroptarget.h',
+  'gtkdroptargetasync.h',
   'gtkeditable.h',
   'gtkemojichooser.h',
   'gtkentry.h',
diff --git a/tests/testdnd.c b/tests/testdnd.c
index f48a55b6af..ec1a5c2a08 100644
--- a/tests/testdnd.c
+++ b/tests/testdnd.c
@@ -287,138 +287,87 @@ static const char * trashcan_open_xpm[] = {
 GdkPixbuf *trashcan_open;
 GdkPixbuf *trashcan_closed;
 
-gboolean have_drag;
 
-static const char *target_table[] = {
-  "STRING",
-  "text/plain",
-  "application/x-rootwindow-drop"
-};
+static GdkDragAction
+action_make_unique (GdkDragAction action)
+{
+  if (gdk_drag_action_is_unique (action))
+    return action;
 
-static guint n_targets = sizeof(target_table) / sizeof(target_table[0]);
+  if (action & GDK_ACTION_COPY)
+    return GDK_ACTION_COPY;
 
-void  
-target_drag_leave (GtkDropTarget *dest,
-                   GdkDrop       *drop,
-                   GtkWidget     *widget)
-{
-  g_print("leave\n");
-  have_drag = FALSE;
-  gtk_image_set_from_pixbuf (GTK_IMAGE (widget), trashcan_closed);
+  if (action & GDK_ACTION_MOVE)
+    return GDK_ACTION_MOVE;
+  
+  if (action & GDK_ACTION_LINK)
+    return GDK_ACTION_LINK;
+  
+  g_assert_not_reached ();
+  return 0;
 }
 
-gboolean
-target_drag_motion (GtkDropTarget *dest,
-                    GdkDrop       *drop,
-                    int            x,
-                    int            y,
-                    GtkWidget     *widget) 
+GdkDragAction
+trash_drag_enter (GtkDropTarget *dest,
+                  GdkDrop       *drop,
+                  double         x,
+                  double         y,
+                  GtkWidget     *widget) 
 {
   char *s;
 
-  if (!have_drag)
-    {
-      have_drag = TRUE;
-      gtk_image_set_from_pixbuf (GTK_IMAGE (widget), trashcan_open);
-    }
+  gtk_image_set_from_pixbuf (GTK_IMAGE (widget), trashcan_open);
 
   s = gdk_content_formats_to_string (gdk_drop_get_formats (drop));
-  g_print ("%s\n", s);
-
-  gdk_drop_status (drop, GDK_ACTION_ALL);
+  g_print ("trash enter: %s\n", s);
+  g_free (s);
 
-  return TRUE;
+  return action_make_unique (gdk_drop_get_actions (drop));;
 }
 
-static void
-got_text_in_target (GObject *object,
-                    GAsyncResult *result,
-                    gpointer data)
-{
-  char *str;
-
-  str = gdk_drop_read_text_finish (GDK_DROP (object), result, NULL);
-  if (str)
-    {
-      g_print ("Received \"%s\" in target\n", str);
-      g_free (str);
-    }
-
-  gdk_drop_finish (GDK_DROP (object), GDK_ACTION_MOVE);
-}
- 
-gboolean
-target_drag_drop (GtkDropTarget *dest,
+GdkDragAction
+trash_drag_leave (GtkDropTarget *dest,
                   GdkDrop       *drop,
-                  int            x,
-                  int            y,
-                  GtkWidget     *widget)
+                  GtkWidget     *widget) 
 {
-  GdkContentFormats *formats;
-  const char *format;
-
-  g_print("drop\n");
-  have_drag = FALSE;
+  char *s;
 
   gtk_image_set_from_pixbuf (GTK_IMAGE (widget), trashcan_closed);
 
-  formats = gdk_drop_get_formats (drop);
-  format = gdk_content_formats_match_mime_type (formats, formats);
-  if (format)
-    {
-      gdk_drop_read_text_async (drop, NULL, got_text_in_target, widget);
-      return TRUE;
-    }
-  
-  gdk_drop_status (drop, 0);
+  s = gdk_content_formats_to_string (gdk_drop_get_formats (drop));
+  g_print ("trash leave: %s\n", s);
+  g_free (s);
 
-  return FALSE;
+  return action_make_unique (gdk_drop_get_actions (drop));
 }
 
-static GdkDragAction
-action_make_unique (GdkDragAction action)
+gboolean
+trash_drag_drop (GtkDropTarget *dest,
+                 GdkDrop       *drop,
+                 double         x,
+                 double         y,
+                 GtkWidget     *widget)
 {
-  if (gdk_drag_action_is_unique (action))
-    return action;
-
-  if (action & GDK_ACTION_COPY)
-    return GDK_ACTION_COPY;
+  char *s;
 
-  if (action & GDK_ACTION_MOVE)
-    return GDK_ACTION_MOVE;
-  
-  if (action & GDK_ACTION_LINK)
-    return GDK_ACTION_LINK;
-  
-  g_assert_not_reached ();
-  return 0;
-}
+  s = gdk_content_formats_to_string (gdk_drop_get_formats (drop));
+  g_print ("trash drop: %s\n", s);
+  g_free (s);
 
-static void
-got_text (GObject *object,
-          GAsyncResult *result,
-          gpointer data)
-{
-  char *str;
+  gdk_drop_finish (drop, action_make_unique (gdk_drop_get_actions (drop)));
 
-  str = gdk_drop_read_text_finish (GDK_DROP (object), result, NULL);
-  if (str)
-    {
-      g_print ("Received \"%s\" in label\n", str);
-      g_free (str);
-    }
+  return TRUE;
 }
- 
-void  
+
+gboolean
 label_drag_drop (GtkDropTarget *dest,
-                 GdkDrop       *drop,
+                 const GValue  *value,
                  int            x,
                  int            y,
                  GtkWidget     *widget)
 {
-  gdk_drop_read_text_async (drop, NULL, got_text, widget);
-  GdkDragAction action = action_make_unique (gdk_drop_get_actions (drop));
-  gdk_drop_finish (drop, action);
+  g_print ("Received \"%s\" in label\n", g_value_get_string (value));
+  return TRUE;
 }
 
 /* The following is a rather elaborate example demonstrating/testing
@@ -443,18 +392,10 @@ popdown_cb (gpointer data)
   return FALSE;
 }
 
-gboolean
-popup_motion (GtkDropTarget *dest,
-              GdkDrop       *drop)
-{
-  gdk_drop_status (drop, GDK_ACTION_COPY);
-  return TRUE;
-}
-
 void  
 popup_enter (GtkDropTarget *dest)
 {
-g_print ("popup enter\n");
+  g_print ("popup enter\n");
   if (!in_popup)
     {
       in_popup = TRUE;
@@ -483,10 +424,8 @@ g_print ("popup leave\n");
 }
 
 static gboolean
-popup_drop (GtkDropTarget *dest,
-            GdkDrop       *drop)
+popup_drop (GtkDropTarget *dest)
 {
-  gdk_drop_finish (drop, GDK_ACTION_COPY);
   popdown_cb (NULL);
   return TRUE;
 }
@@ -501,12 +440,10 @@ popup_cb (gpointer data)
          GtkWidget *button;
          GtkWidget *grid;
          int i, j;
-          GdkContentFormats *targets;
          
          popup_window = gtk_window_new ();
 
          grid = gtk_grid_new ();
-          targets = gdk_content_formats_new_for_gtype (G_TYPE_STRING);
 
          for (i=0; i<3; i++)
            for (j=0; j<3; j++)
@@ -520,16 +457,13 @@ popup_cb (gpointer data)
                 gtk_widget_set_vexpand (button, TRUE);
                gtk_grid_attach (GTK_GRID (grid), button, i, j, 1, 1);
 
-                dest = gtk_drop_target_new (targets, GDK_ACTION_COPY | GDK_ACTION_MOVE);
-               g_signal_connect (dest, "accept", G_CALLBACK (popup_motion), NULL);
-               g_signal_connect (dest, "drag-enter", G_CALLBACK (popup_enter), NULL);
-               g_signal_connect (dest, "drag-leave", G_CALLBACK (popup_leave), NULL);
-               g_signal_connect (dest, "drag-drop", G_CALLBACK (popup_drop), NULL);
+                dest = gtk_drop_target_new (G_TYPE_STRING, GDK_ACTION_COPY | GDK_ACTION_MOVE);
+               g_signal_connect (dest, "enter", G_CALLBACK (popup_enter), NULL);
+               g_signal_connect (dest, "leave", G_CALLBACK (popup_leave), NULL);
+               g_signal_connect (dest, "drop", G_CALLBACK (popup_drop), NULL);
                 gtk_widget_add_controller (button, GTK_EVENT_CONTROLLER (dest));
              }
          gtk_container_add (GTK_CONTAINER (popup_window), grid);
-          gdk_content_formats_unref (targets);
-
        }
       gtk_widget_show (popup_window);
       popped_up = TRUE;
@@ -550,18 +484,17 @@ popsite_motion (GtkDropTarget *dest,
 }
 
 void  
-popsite_enter (GtkDropTarget *dest)
+popsite_enter (GtkDropControllerMotion *motion)
 {
-g_print ("popsite enter\n");
+  g_print ("popsite enter\n");
   if (!popup_timer)
     popup_timer = g_timeout_add (500, popup_cb, NULL);
-
 }
 
 void  
-popsite_leave (GtkDropTarget *dest)
+popsite_leave (GtkDropControllerMotion *motion)
 {
-g_print ("popsite leave\n");
+  g_print ("popsite leave\n");
   if (popup_timer)
     {
       g_source_remove (popup_timer);
@@ -606,8 +539,9 @@ main (int argc, char **argv)
   GdkTexture *texture;
   GdkContentProvider *content;
   GtkDragSource *source;
-  GdkContentFormats *targets;
   GtkDropTarget *dest;
+  GtkDropTargetAsync *async;
+  GtkEventController *controller;
   gboolean done = FALSE;
 
   test_init ();
@@ -630,9 +564,8 @@ main (int argc, char **argv)
   
   label = gtk_label_new ("Drop Here\n");
 
-  targets = gdk_content_formats_new (target_table, n_targets - 1); /* no rootwin */
-  dest = gtk_drop_target_new (gdk_content_formats_ref (targets), GDK_ACTION_COPY | GDK_ACTION_MOVE);
-  g_signal_connect (dest, "drag-drop", G_CALLBACK (label_drag_drop), NULL);
+  dest = gtk_drop_target_new (G_TYPE_STRING, GDK_ACTION_COPY | GDK_ACTION_MOVE);
+  g_signal_connect (dest, "drop", G_CALLBACK (label_drag_drop), NULL);
   gtk_widget_add_controller (label, GTK_EVENT_CONTROLLER (dest));
 
   gtk_widget_set_hexpand (label, TRUE);
@@ -641,22 +574,21 @@ main (int argc, char **argv)
 
   label = gtk_label_new ("Popup\n");
 
-  dest = gtk_drop_target_new (targets, GDK_ACTION_COPY | GDK_ACTION_MOVE);
-  g_signal_connect (dest, "accept", G_CALLBACK (popsite_motion), NULL);
-  g_signal_connect (dest, "drag-enter", G_CALLBACK (popsite_enter), NULL);
-  g_signal_connect (dest, "drag-leave", G_CALLBACK (popsite_leave), NULL);
-  gtk_widget_add_controller (label, GTK_EVENT_CONTROLLER (dest));
+  controller = gtk_drop_controller_motion_new ();
+  g_signal_connect (controller, "enter", G_CALLBACK (popsite_enter), NULL);
+  g_signal_connect (controller, "leave", G_CALLBACK (popsite_leave), NULL);
+  gtk_widget_add_controller (label, controller);
 
   gtk_widget_set_hexpand (label, TRUE);
   gtk_widget_set_vexpand (label, TRUE);
   gtk_grid_attach (GTK_GRID (grid), label, 1, 1, 1, 1);
 
   pixmap = gtk_image_new_from_pixbuf (trashcan_closed);
-  dest = gtk_drop_target_new (NULL, 0);
-  g_signal_connect (dest, "drag-leave", G_CALLBACK (target_drag_leave), pixmap);
-  g_signal_connect (dest, "accept", G_CALLBACK (target_drag_motion), pixmap);
-  g_signal_connect (dest, "drag-drop", G_CALLBACK (target_drag_drop), pixmap);
-  gtk_widget_add_controller (pixmap, GTK_EVENT_CONTROLLER (dest));
+  async = gtk_drop_target_async_new (NULL, 0);
+  g_signal_connect (async, "drag-enter", G_CALLBACK (trash_drag_enter), pixmap);
+  g_signal_connect (async, "drag-leave", G_CALLBACK (trash_drag_leave), pixmap);
+  g_signal_connect (async, "drop", G_CALLBACK (trash_drag_drop), pixmap);
+  gtk_widget_add_controller (pixmap, GTK_EVENT_CONTROLLER (async));
 
   gtk_widget_set_hexpand (pixmap, TRUE);
   gtk_widget_set_vexpand (pixmap, TRUE);
diff --git a/tests/testdnd2.c b/tests/testdnd2.c
index 17b1abcbbb..2294b2539e 100644
--- a/tests/testdnd2.c
+++ b/tests/testdnd2.c
@@ -128,7 +128,7 @@ static void
 perform_drop (GdkDrop   *drop,
               GtkWidget *image)
 {
-  if (gdk_drop_has_value (drop, GDK_TYPE_TEXTURE))
+  if (gdk_content_formats_contain_gtype (gdk_drop_get_formats (drop), GDK_TYPE_TEXTURE))
     gdk_drop_read_value_async (drop, GDK_TYPE_TEXTURE, G_PRIORITY_DEFAULT, NULL, got_texture, image);
   else
     {
@@ -192,39 +192,37 @@ ask_actions (GdkDrop *drop,
 static gboolean
 delayed_deny (gpointer data)
 {
-  GtkDropTarget *dest = data;
+  GtkDropTargetAsync *dest = data;
   GtkWidget *image = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest));
   GdkDrop *drop = GDK_DROP (g_object_get_data (G_OBJECT (image), "drop"));
 
   if (drop)
     {
       g_print ("denying drop, late\n");
-      gtk_drop_target_deny_drop (dest, drop);
+      gtk_drop_target_async_reject_drop (dest, drop);
     }
 
   return G_SOURCE_REMOVE;
 }
 
 static gboolean
-image_drag_motion (GtkDropTarget    *dest,
-                   GdkDrop          *drop,
-                   gpointer          data)
+image_drag_accept (GtkDropTargetAsync *dest,
+                   GdkDrop            *drop,
+                   gpointer            data)
 {
   GtkWidget *image = data;
   g_object_set_data_full (G_OBJECT (image), "drop", g_object_ref (drop), g_object_unref);
 
   g_timeout_add (1000, delayed_deny, dest);
 
-  gdk_drop_status (drop, gtk_drop_target_get_actions (dest));
-
   return TRUE;
 }
 
 static gboolean
 image_drag_drop (GtkDropTarget    *dest,
                  GdkDrop          *drop,
-                 int               x,
-                 int               y,
+                 double            x,
+                 double            y,
                  gpointer          data)
 {
   GtkWidget *image = data;
@@ -330,7 +328,7 @@ make_image (const gchar *icon_name, int hotspot)
 {
   GtkWidget *image;
   GtkDragSource *source;
-  GtkDropTarget *dest;
+  GtkDropTargetAsync *dest;
   GdkContentFormats *formats;
   GdkContentFormatsBuilder *builder;
 
@@ -352,9 +350,9 @@ make_image (const gchar *icon_name, int hotspot)
   g_signal_connect (source, "drag-cancel", G_CALLBACK (drag_cancel), NULL);
   gtk_widget_add_controller (image, GTK_EVENT_CONTROLLER (source));
 
-  dest = gtk_drop_target_new (formats, GDK_ACTION_COPY|GDK_ACTION_MOVE|GDK_ACTION_ASK);
-  g_signal_connect (dest, "accept", G_CALLBACK (image_drag_motion), image);
-  g_signal_connect (dest, "drag-drop", G_CALLBACK (image_drag_drop), image);
+  dest = gtk_drop_target_async_new (formats, GDK_ACTION_COPY|GDK_ACTION_MOVE|GDK_ACTION_ASK);
+  g_signal_connect (dest, "accept", G_CALLBACK (image_drag_accept), image);
+  g_signal_connect (dest, "drop", G_CALLBACK (image_drag_drop), image);
   gtk_widget_add_controller (image, GTK_EVENT_CONTROLLER (dest));
 
   return image;
diff --git a/tests/testdnd3.c b/tests/testdnd3.c
index 4b58ad136b..795445f65c 100644
--- a/tests/testdnd3.c
+++ b/tests/testdnd3.c
@@ -50,12 +50,6 @@ drag_cancel (GtkDragSource       *source,
   drag_end (source, drag);
 }
 
-typedef struct {
-  GtkWidget *canvas;
-  double x;
-  double y;
-} DropData;
-
 typedef struct {
   double x, y;
   double angle;
@@ -76,32 +70,22 @@ apply_transform (GtkWidget *item)
   gsk_transform_unref (transform);
 }
 
-static void
-got_data (GObject      *source,
-          GAsyncResult *result,
-          gpointer      user_data)
+static gboolean
+drag_drop (GtkDropTarget *target,
+           const GValue  *value,
+           double         x,
+           double         y)
 {
-  GdkDrop *drop = GDK_DROP (source);
-  DropData *data = user_data;
   GtkWidget *item;
-  const GValue *value;
   TransformData *transform_data;
   GtkWidget *canvas;
   GtkWidget *last_child;
 
-  value = gdk_drop_read_value_finish (drop, result, NULL);
-  if (value == NULL)
-    {
-      gdk_drop_finish (drop, 0);
-      return;
-    }
-
   item = g_value_get_object (value);
-
   transform_data = g_object_get_data (G_OBJECT (item), "transform-data");
 
-  transform_data->x = data->x;
-  transform_data->y = data->y;
+  transform_data->x = x;
+  transform_data->y = y;
 
   canvas = gtk_widget_get_parent (item);
   last_child = gtk_widget_get_last_child (canvas);
@@ -110,26 +94,6 @@ got_data (GObject      *source,
 
   apply_transform (item);
 
-  gdk_drop_finish (drop, GDK_ACTION_MOVE);
-
-  g_free (data);
-}
-
-static gboolean
-drag_drop (GtkDropTarget *dest,
-           GdkDrop       *drop,
-           int            x,
-           int            y)
-{
-  DropData *data;
-
-  data = g_new (DropData, 1);
-  data->canvas = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest));
-  data->x = x;
-  data->y = y;
-
-  gdk_drop_read_value_async (drop, GTK_TYPE_WIDGET, G_PRIORITY_DEFAULT, NULL, got_data, data);
-
   return TRUE;
 }
 
@@ -153,8 +117,8 @@ canvas_new (void)
   g_signal_connect (source, "drag-cancel", G_CALLBACK (drag_cancel), NULL);
   gtk_widget_add_controller (canvas, GTK_EVENT_CONTROLLER (source));
 
-  dest = gtk_drop_target_new (gdk_content_formats_new_for_gtype (GTK_TYPE_WIDGET), GDK_ACTION_MOVE);
-  g_signal_connect (dest, "drag-drop", G_CALLBACK (drag_drop), NULL);
+  dest = gtk_drop_target_new (GTK_TYPE_WIDGET, GDK_ACTION_MOVE);
+  g_signal_connect (dest, "drop", G_CALLBACK (drag_drop), NULL);
   gtk_widget_add_controller (canvas, GTK_EVENT_CONTROLLER (dest));
 
   return canvas;
@@ -186,31 +150,16 @@ set_color (GtkWidget *item,
   g_free (css);
 }
 
-static void
-got_color (GObject *source,
-           GAsyncResult *result,
-           gpointer data)
+static gboolean
+item_drag_drop (GtkDropTarget *dest,
+                const GValue  *value,
+                double         x,
+                double         y)
 {
-  GdkDrop *drop = GDK_DROP (source);
-  GtkDropTarget *dest = data;
   GtkWidget *item = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest));
-  const GValue *value;
-  GdkRGBA *color;
-
-  value = gdk_drop_read_value_finish (drop, result, NULL);
-  color = g_value_get_boxed (value);
-  set_color (item, color);
 
-  gdk_drop_finish (drop, GDK_ACTION_COPY);
-}
+  set_color (item, g_value_get_boxed (value));
 
-static gboolean
-item_drag_drop (GtkDropTarget *dest,
-                GdkDrop       *drop,
-               int            x,
-               int            y)
-{
-  gdk_drop_read_value_async (drop, GDK_TYPE_RGBA, G_PRIORITY_DEFAULT, NULL, got_color, dest);
   return TRUE;
 }
 
@@ -261,7 +210,6 @@ canvas_item_new (int i,
   TransformData *transform_data;
   GdkRGBA rgba;
   GtkDropTarget *dest;
-  GdkContentFormats *formats;
   GtkGesture *gesture;
 
   label = g_strdup_printf ("Item %d", i);
@@ -283,11 +231,9 @@ canvas_item_new (int i,
   g_free (label);
   g_free (id);
 
-  formats = gdk_content_formats_new_for_gtype (GDK_TYPE_RGBA);
-  dest = gtk_drop_target_new (formats, GDK_ACTION_COPY);
-  g_signal_connect (dest, "drag-drop", G_CALLBACK (item_drag_drop), NULL);
+  dest = gtk_drop_target_new (GDK_TYPE_RGBA, GDK_ACTION_COPY);
+  g_signal_connect (dest, "drop", G_CALLBACK (item_drag_drop), NULL);
   gtk_widget_add_controller (widget, GTK_EVENT_CONTROLLER (dest));
-  gdk_content_formats_unref (formats);
 
   gesture = gtk_gesture_rotate_new ();
   g_signal_connect (gesture, "angle-changed", G_CALLBACK (angle_changed), NULL);
diff --git a/tests/testlist3.c b/tests/testlist3.c
index 1e2825de2b..88d1713716 100644
--- a/tests/testlist3.c
+++ b/tests/testlist3.c
@@ -29,47 +29,30 @@ drag_begin (GtkDragSource *source,
   g_object_unref (paintable);
 }
 
-static void
-got_row (GObject      *src,
-         GAsyncResult *result,
-         gpointer      data)
+static gboolean
+drag_drop (GtkDropTarget *dest,
+           const GValue  *value,
+           double         x,
+           double         y,
+           gpointer       data)
 {
-  GdkDrop *drop = GDK_DROP (src);
   GtkWidget *target = data;
   GtkWidget *source;
   int pos;
 
-  source = g_value_get_object (gdk_drop_read_value_finish (drop, result, NULL));
+  source = g_value_get_object (value);
   if (source == NULL)
-    {
-      gdk_drop_finish (drop, 0);
-      return;
-    }
+    return FALSE;
 
   pos = gtk_list_box_row_get_index (GTK_LIST_BOX_ROW (target));
   if (source == target)
-    {
-      gdk_drop_finish (drop, 0);
-      return;
-    }
+    return FALSE;
 
   g_object_ref (source);
   gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (source)), source);
   gtk_list_box_insert (GTK_LIST_BOX (gtk_widget_get_parent (target)), source, pos);
   g_object_unref (source);
 
-  gdk_drop_finish (drop, GDK_ACTION_MOVE);
-}
-
-static gboolean
-drag_drop (GtkDropTarget    *dest,
-           GdkDrop          *drop,
-           int               x,
-           int               y,
-           gpointer          data)
-{
-  gdk_drop_read_value_async (drop, GTK_TYPE_LIST_BOX_ROW, G_PRIORITY_DEFAULT, NULL, got_row, data);
-
   return TRUE;
 }
 
@@ -96,8 +79,8 @@ create_row (const gchar *text)
   g_signal_connect (source, "prepare", G_CALLBACK (prepare), row);
   gtk_widget_add_controller (image, GTK_EVENT_CONTROLLER (source));
 
-  dest = gtk_drop_target_new (gdk_content_formats_new_for_gtype (GTK_TYPE_LIST_BOX_ROW), GDK_ACTION_MOVE);
-  g_signal_connect (dest, "drag-drop", G_CALLBACK (drag_drop), row);
+  dest = gtk_drop_target_new (GTK_TYPE_LIST_BOX_ROW, GDK_ACTION_MOVE);
+  g_signal_connect (dest, "drop", G_CALLBACK (drag_drop), row);
   gtk_widget_add_controller (GTK_WIDGET (row), GTK_EVENT_CONTROLLER (dest));
 
   return row;
diff --git a/tests/testnotebookdnd.c b/tests/testnotebookdnd.c
index f7050f04a9..d8d8077d63 100644
--- a/tests/testnotebookdnd.c
+++ b/tests/testnotebookdnd.c
@@ -55,10 +55,6 @@ gchar *tabs4 [] = {
   NULL
 };
 
-static const char *button_targets[] = {
-  "GTK_NOTEBOOK_TAB"
-};
-
 static GtkNotebook*
 window_creation_function (GtkNotebook *source_notebook,
                           GtkWidget   *child,
@@ -93,7 +89,8 @@ on_page_reordered (GtkNotebook *notebook, GtkWidget *child, guint page_num, gpoi
 static gboolean
 remove_in_idle (gpointer data)
 {
-  GtkWidget *child = data;
+  GtkNotebookPage *page = data;
+  GtkWidget *child = gtk_notebook_page_get_child (page);
   GtkWidget *parent = gtk_widget_get_ancestor (child, GTK_TYPE_NOTEBOOK);
   GtkWidget *tab_label;
 
@@ -104,44 +101,17 @@ remove_in_idle (gpointer data)
   return G_SOURCE_REMOVE;
 }
 
-static void
-got_page (GObject *source,
-          GAsyncResult *result,
-          gpointer data)
-{
-  GdkDrop *drop = GDK_DROP (source);
-  GInputStream *stream;
-  const char *mime_type;
-
-  stream = gdk_drop_read_finish (drop, result, &mime_type, NULL);
-
-  if (stream)
-    {
-      GBytes *bytes;
-      GtkWidget **child;
-
-      bytes = g_input_stream_read_bytes (stream, sizeof (gpointer), NULL, NULL);
-      child = (gpointer)g_bytes_get_data (bytes, NULL);
-
-      g_idle_add (remove_in_idle, *child);
-
-      gdk_drop_finish (drop, GDK_ACTION_MOVE);
-
-      g_bytes_unref (bytes);
-      g_object_unref (stream);
-    }
-  else
-    gdk_drop_finish (drop, 0);
-}
-
 static gboolean
 on_button_drag_drop (GtkDropTarget *dest,
-                     GdkDrop       *drop,
+                     const GValue  *value,
+                     double         x,
+                     double         y,
                      gpointer       user_data)
 {
-  gdk_drop_read_async (drop, (const char *[]) { "GTK_NOTEBOOK_TAB", NULL }, G_PRIORITY_DEFAULT, NULL, 
got_page, NULL);
+  GtkNotebookPage *page;
 
-  gdk_drop_finish (drop, GDK_ACTION_MOVE);
+  page = g_value_get_object (value);
+  g_idle_add (remove_in_idle, page);
 
   return TRUE;
 }
@@ -298,8 +268,8 @@ create_trash_button (void)
 
   button = gtk_button_new_with_mnemonic ("_Delete");
 
-  dest = gtk_drop_target_new (gdk_content_formats_new (button_targets, G_N_ELEMENTS (button_targets)), 
GDK_ACTION_MOVE);
-  g_signal_connect (dest, "drag-drop", G_CALLBACK (on_button_drag_drop), NULL);
+  dest = gtk_drop_target_new (GTK_TYPE_NOTEBOOK_PAGE, GDK_ACTION_MOVE);
+  g_signal_connect (dest, "drop", G_CALLBACK (on_button_drag_drop), NULL);
   gtk_widget_add_controller (button, GTK_EVENT_CONTROLLER (dest));
 
   return button;
diff --git a/tests/testtreednd.c b/tests/testtreednd.c
index c087527ce3..04450938e2 100644
--- a/tests/testtreednd.c
+++ b/tests/testtreednd.c
@@ -84,32 +84,16 @@ get_dragsource (void)
   return GTK_WIDGET (tv);
 }
 
-static void
-got_text (GObject      *source,
-          GAsyncResult *result,
-          gpointer      data)
-{
-  GdkDrop *drop = GDK_DROP (source);
-  GtkWidget *widget = data;
-  const GValue *value;
-
-  value = gdk_drop_read_value_finish (drop, result, NULL);
-  if (value == NULL)
-    return;
-  
-  gtk_label_set_label (GTK_LABEL (widget), g_value_get_string (value));
-}
-
 static void
 drag_drop (GtkDropTarget *dest,
-           GdkDrop       *drop,
+           const GValue  *value,
            int            x,
            int            y,
            gpointer       dada)
 {
   GtkWidget *widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest));
 
-  gdk_drop_read_value_async (drop, G_TYPE_STRING, G_PRIORITY_DEFAULT, NULL, got_text, widget);
+  gtk_label_set_label (GTK_LABEL (widget), g_value_get_string (value));
 }
 
 static GtkWidget *
@@ -119,8 +103,8 @@ get_droptarget (void)
   GtkDropTarget *dest;
 
   label = gtk_label_new ("Drop here");
-  dest = gtk_drop_target_new (gdk_content_formats_new_for_gtype (G_TYPE_STRING), GDK_ACTION_COPY);
-  g_signal_connect (dest, "drag-drop", G_CALLBACK (drag_drop), NULL);
+  dest = gtk_drop_target_new (G_TYPE_STRING, GDK_ACTION_COPY);
+  g_signal_connect (dest, "drop", G_CALLBACK (drag_drop), NULL);
   gtk_widget_add_controller (label, GTK_EVENT_CONTROLLER (dest));
 
   return label;
diff --git a/testsuite/gtk/defaultvalue.c b/testsuite/gtk/defaultvalue.c
index 29536fe037..ab735dc482 100644
--- a/testsuite/gtk/defaultvalue.c
+++ b/testsuite/gtk/defaultvalue.c
@@ -150,7 +150,8 @@ test_type (gconstpointer data)
 
       /* These are set in init() */
       if ((g_type_is_a (type, GDK_TYPE_CLIPBOARD) ||
-           g_type_is_a (type, GDK_TYPE_CONTENT_PROVIDER)) &&
+           g_type_is_a (type, GDK_TYPE_CONTENT_PROVIDER) ||
+           g_type_is_a (type, GTK_TYPE_DROP_TARGET)) &&
          strcmp (pspec->name, "formats") == 0)
        continue;
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]