Up: [README.md](../README.md), Prev: [Section 27](sec27.md), Next: [Section 29](sec29.md) # Drag and drop ## What's drag and drop? Drag and drop is also written as "Drag-and-Drop", or "DND" in short. DND is like "copy and paste" or "cut and paste". If a user drags a UI element, which is a widget, selected part or something, data is transferred from the source to the destination. You probably have experience that you moved a file with DND. ![DND on the GUI file manager](../image/dnd.png) When the DND starts, the file `sample_file.txt` is given to the system. When the DND ends, the system gives `sample_file.txt` to the directory `sample_folder` in the file manager. Therefore, it is like "cut and paste". The actual behavior may be different from the explanation here, but the concept is similar. ## Example for DND This tutorial provides a simple example in the `src/dnd` directory. It has three labels for the source and one label for the destination. The source labels have "red", "green" or "blue" labels. If a user drags the label to the destination label, the font color will be changed. ![DND example](../image/dnd_canvas.png) ## UI file The widgets are defined in the XML file `dnd.ui`. ~~~xml 1 2 3 4 800 5 600 6 FALSE 7 Drag and Drop 8 9 10 TRUE 11 TRUE 12 GTK_ORIENTATION_VERTICAL 13 5 14 15 16 GTK_ORIENTATION_HORIZONTAL 17 TRUE 18 19 20 RED 21 GTK_JUSTIFY_CENTER 22 red 23 24 25 26 27 GREEN 28 GTK_JUSTIFY_CENTER 29 green 30 31 32 33 34 BLUE 35 GTK_JUSTIFY_CENTER 36 blue 37 38 39 40 41 42 43 CANVAS 44 GTK_JUSTIFY_CENTER 45 canvas 46 TRUE 47 TRUE 48 49 50 51 52 53 54 ~~~ It is converted to a resource file by `glib-compile-resources`. The compiler uses an XML file `dnd.gresource.xml`. ~~~xml 1 2 3 4 dnd.ui 5 6 ~~~ ## C file dnd.c The C file `dnd.c` isn't a big file. The number of the lines is less than a hundred. A GtkApplication object is created in the function `main`. ~~~C 1 int 2 main (int argc, char **argv) { 3 GtkApplication *app; 4 int stat; 5 6 app = gtk_application_new (APPLICATION_ID, G_APPLICATION_DEFAULT_FLAGS); 7 g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL); 8 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL); 9 stat =g_application_run (G_APPLICATION (app), argc, argv); 10 g_object_unref (app); 11 return stat; 12 } ~~~ The application ID is defined as: ```C #define APPLICATION_ID "com.github.ToshioCP.dnd" ``` ### Startup signal handler Most of the work is done in the "startup" signal handler. Two objects GtkDragSource and GtkDropTarget is used for DND implementation. - Drag source: A drag source (GtkDragSource instance) is an event controller. It initiates a DND operation when the user clicks and drags the widget. If a data, in the form of GdkContentProvider, is set in advance, it gives the data to the system at the beginning of the drag. - Drop target: A drop target (GtkDropTarget) is also an event controller. You can get the data in the GtkDropTarget::drop signal handler. The example below uses these objects in a very simple way. You can use number of features that the two objects have. See the following links for more information. - [Drag-and-Drop in GTK](https://docs.gtk.org/gtk4/drag-and-drop.html) - [GtkDragSource](https://docs.gtk.org/gtk4/class.DragSource.html) - [GtkDropTarget](https://docs.gtk.org/gtk4/class.DropTarget.html) ~~~C 1 static void 2 app_startup (GApplication *application) { 3 GtkApplication *app = GTK_APPLICATION (application); 4 GtkBuilder *build; 5 GtkWindow *win; 6 GtkLabel *src_labels[3]; 7 int i; 8 GtkLabel *canvas; 9 GtkDragSource *src; 10 GdkContentProvider* content; 11 GtkDropTarget *tgt; 12 GdkDisplay *display; 13 char *s; 14 15 build = gtk_builder_new_from_resource ("/com/github/ToshioCP/dnd/dnd.ui"); 16 win = GTK_WINDOW (gtk_builder_get_object (build, "win")); 17 src_labels[0] = GTK_LABEL (gtk_builder_get_object (build, "red")); 18 src_labels[1] = GTK_LABEL (gtk_builder_get_object (build, "green")); 19 src_labels[2] = GTK_LABEL (gtk_builder_get_object (build, "blue")); 20 canvas = GTK_LABEL (gtk_builder_get_object (build, "canvas")); 21 gtk_window_set_application (win, app); 22 g_object_unref (build); 23 24 for (i=0; i<3; ++i) { 25 src = gtk_drag_source_new (); 26 content = gdk_content_provider_new_typed (G_TYPE_STRING, gtk_widget_get_name (GTK_WIDGET (src_labels[i]))); 27 gtk_drag_source_set_content (src, content); 28 g_object_unref (content); 29 gtk_widget_add_controller (GTK_WIDGET (src_labels[i]), GTK_EVENT_CONTROLLER (src)); // The ownership of src is taken by the instance. 30 } 31 32 tgt = gtk_drop_target_new (G_TYPE_STRING, GDK_ACTION_COPY); 33 g_signal_connect (tgt, "drop", G_CALLBACK (drop_cb), NULL); 34 gtk_widget_add_controller (GTK_WIDGET (canvas), GTK_EVENT_CONTROLLER (tgt)); // The ownership of tgt is taken by the instance. 35 36 provider = gtk_css_provider_new (); 37 s = g_strdup_printf (format, "black"); 38 gtk_css_provider_load_from_data (provider, s, -1); 39 g_free (s); 40 display = gdk_display_get_default (); 41 gtk_style_context_add_provider_for_display (display, GTK_STYLE_PROVIDER (provider), 42 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); 43 g_object_unref (provider); // The provider is still alive because the display owns it. 44 } ~~~ - 15-22: Builds the widgets. The array `source_labels[]` points the source labels red, green and blue in the ui file. The variable `canvas` points the destination label. - 24-30: Sets the DND source widgets. The for-loop carries out through the array `src_labels[]` each of which points the source widget, red, green or blue label. - 25: Creates a new GtkDragSource instance. - 26: Creates a new GdkContentProvider instance with the string "red", "green" or "blue. They are the name of the widgets. These strings are the data to transfer through the DND operation. - 27: Sets the content of the drag source to the GdkContentProvider instance above. - 28: Content is useless so it is destroyed. - 29: Add the event controller, which is actually the drag source, to the widget. If a DND operation starts on the widget, the corresponding drag source works and the data is given to the system. - 32-34: Sets the DND drop target. - 32: Creates a new GtkDropTarget instance. The first parameter is the GType of the data. The second parameter is a GdkDragAction enumerate constant. The arguments here are string type and the constant for copy. - 33: Connects the "drop" signal and the handler `drop_cb`. - 34: Add the event controller, which is actually the drop target, to the widget. - 36-43: Sets CSS. - 37: A varable `format` is static and defined at the top of the program. Static variables are shown below. ```C static GtkCssProvider *provider = NULL; static const char *format = "label {padding: 20px;} label#red {background: red;} " "label#green {background: green;} label#blue {background: blue;} " "label#canvas {color: %s; font-weight: bold; font-size: 72pt;}"; ``` ### Activate signal handler ~~~C 1 static void 2 app_activate (GApplication *application) { 3 GtkApplication *app = GTK_APPLICATION (application); 4 GtkWindow *win; 5 6 win = gtk_application_get_active_window (app); 7 gtk_window_present (win); 8 } ~~~ This handler just shows the window. ### Drop signal handler ~~~C 1 static gboolean 2 drop_cb (GtkDropTarget* self, const GValue* value, gdouble x, gdouble y, gpointer user_data) { 3 char *s; 4 5 s = g_strdup_printf (format, g_value_get_string (value)); 6 gtk_css_provider_load_from_data (provider, s, -1); 7 g_free (s); 8 return TRUE; 9 } ~~~ The "drop" signal handler has five parameters. - GtkDropTarget instance on which the signal has been emitted. - GValue that holds the data from the source. - The arguments `x` and `y` are the coordinate of the mouse when released. - User data was set when the signal and handler was connected. The string from the GValue is "red", "green" or "blue". It replaces "%s" in the variable `format`. That means the font color of the label `canvas` will turn to the color. ## Meson.build The file `meson.build` controls the building process. ~~~meson 1 project('dnd', 'c') 2 3 gtkdep = dependency('gtk4') 4 5 gnome = import('gnome') 6 resources = gnome.compile_resources('resources','dnd.gresource.xml') 7 8 executable(meson.project_name(), 'dnd.c', resources, dependencies: gtkdep, export_dynamic: true, install: false) ~~~ You can build it from the command line. ``` $ cd src/dnd $ meson setup _build $ ninja -C _build $ _build/dnd ``` The source files are under the directory `src/dnd` of the [repository](https://github.com/ToshioCP/Gtk4-tutorial). Download it and see the directory. Up: [README.md](../README.md), Prev: [Section 27](sec27.md), Next: [Section 29](sec29.md)