2023-01-18 06:37:14 +01:00
|
|
|
Up: [README.md](../README.md), Prev: [Section 27](sec27.md), Next: [Section 29](sec29.md)
|
2021-06-15 04:24:42 +02:00
|
|
|
|
2023-07-29 14:41:57 +02:00
|
|
|
# Drag and drop
|
2021-06-15 04:24:42 +02:00
|
|
|
|
2023-07-29 14:41:57 +02:00
|
|
|
## What's drag and drop?
|
2021-06-15 04:24:42 +02:00
|
|
|
|
2023-07-29 14:41:57 +02:00
|
|
|
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.
|
2021-06-15 04:24:42 +02:00
|
|
|
|
2023-07-29 14:41:57 +02:00
|
|
|
You probably have experience that you moved a file with DND.
|
2021-06-15 04:24:42 +02:00
|
|
|
|
2023-07-29 14:41:57 +02:00
|
|
|
![DND on the GUI file manager](../image/dnd.png)
|
2021-06-15 04:24:42 +02:00
|
|
|
|
2023-07-29 14:41:57 +02:00
|
|
|
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.
|
2021-06-15 04:24:42 +02:00
|
|
|
|
2023-07-29 14:41:57 +02:00
|
|
|
## Example for DND
|
2022-04-05 10:42:02 +02:00
|
|
|
|
2023-07-29 14:41:57 +02:00
|
|
|
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.
|
2022-04-05 10:42:02 +02:00
|
|
|
|
2023-07-29 14:41:57 +02:00
|
|
|
![DND example](../image/dnd_canvas.png)
|
2021-06-15 04:24:42 +02:00
|
|
|
|
2023-07-29 14:41:57 +02:00
|
|
|
## UI file
|
2023-01-18 06:37:14 +01:00
|
|
|
|
2023-07-29 14:41:57 +02:00
|
|
|
The widgets are defined in the XML file `dnd.ui`.
|
2021-06-15 04:24:42 +02:00
|
|
|
|
2023-07-29 14:41:57 +02:00
|
|
|
~~~xml
|
|
|
|
1 <?xml version="1.0" encoding="UTF-8"?>
|
|
|
|
2 <interface>
|
|
|
|
3 <object class="GtkApplicationWindow" id="win">
|
|
|
|
4 <property name="default-width">800</property>
|
|
|
|
5 <property name="default-height">600</property>
|
|
|
|
6 <property name="resizable">FALSE</property>
|
|
|
|
7 <property name="title">Drag and Drop</property>
|
|
|
|
8 <child>
|
|
|
|
9 <object class="GtkBox">
|
|
|
|
10 <property name="hexpand">TRUE</property>
|
|
|
|
11 <property name="vexpand">TRUE</property>
|
|
|
|
12 <property name="orientation">GTK_ORIENTATION_VERTICAL</property>
|
|
|
|
13 <property name="spacing">5</property>
|
|
|
|
14 <child>
|
|
|
|
15 <object class="GtkBox">
|
|
|
|
16 <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
|
|
|
|
17 <property name="homogeneous">TRUE</property>
|
|
|
|
18 <child>
|
|
|
|
19 <object class="GtkLabel" id="red">
|
|
|
|
20 <property name="label">RED</property>
|
|
|
|
21 <property name="justify">GTK_JUSTIFY_CENTER</property>
|
|
|
|
22 <property name="name">red</property>
|
|
|
|
23 </object>
|
|
|
|
24 </child>
|
|
|
|
25 <child>
|
|
|
|
26 <object class="GtkLabel" id="green">
|
|
|
|
27 <property name="label">GREEN</property>
|
|
|
|
28 <property name="justify">GTK_JUSTIFY_CENTER</property>
|
|
|
|
29 <property name="name">green</property>
|
|
|
|
30 </object>
|
|
|
|
31 </child>
|
|
|
|
32 <child>
|
|
|
|
33 <object class="GtkLabel" id="blue">
|
|
|
|
34 <property name="label">BLUE</property>
|
|
|
|
35 <property name="justify">GTK_JUSTIFY_CENTER</property>
|
|
|
|
36 <property name="name">blue</property>
|
|
|
|
37 </object>
|
|
|
|
38 </child>
|
|
|
|
39 </object>
|
|
|
|
40 </child>
|
|
|
|
41 <child>
|
|
|
|
42 <object class="GtkLabel" id="canvas">
|
|
|
|
43 <property name="label">CANVAS</property>
|
|
|
|
44 <property name="justify">GTK_JUSTIFY_CENTER</property>
|
|
|
|
45 <property name="name">canvas</property>
|
|
|
|
46 <property name="hexpand">TRUE</property>
|
|
|
|
47 <property name="vexpand">TRUE</property>
|
|
|
|
48 </object>
|
|
|
|
49 </child>
|
|
|
|
50 </object>
|
|
|
|
51 </child>
|
|
|
|
52 </object>
|
|
|
|
53 </interface>
|
|
|
|
54
|
2022-04-05 10:42:02 +02:00
|
|
|
~~~
|
2021-06-15 04:24:42 +02:00
|
|
|
|
2023-07-29 14:41:57 +02:00
|
|
|
It is converted to a resource file by `glib-compile-resources`.
|
|
|
|
The compiler uses an XML file `dnd.gresource.xml`.
|
2021-06-15 04:24:42 +02:00
|
|
|
|
2023-07-15 10:27:53 +02:00
|
|
|
~~~xml
|
2023-07-29 14:41:57 +02:00
|
|
|
1 <?xml version="1.0" encoding="UTF-8"?>
|
|
|
|
2 <gresources>
|
|
|
|
3 <gresource prefix="/com/github/ToshioCP/dnd">
|
|
|
|
4 <file>dnd.ui</file>
|
|
|
|
5 </gresource>
|
|
|
|
6 </gresources>
|
2021-06-15 04:24:42 +02:00
|
|
|
~~~
|
|
|
|
|
2023-07-29 14:41:57 +02:00
|
|
|
## C file dnd.c
|
2021-06-15 04:24:42 +02:00
|
|
|
|
2023-07-29 14:41:57 +02:00
|
|
|
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`.
|
2022-04-05 10:42:02 +02:00
|
|
|
|
2023-07-29 14:41:57 +02:00
|
|
|
~~~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 }
|
2022-04-05 10:42:02 +02:00
|
|
|
~~~
|
|
|
|
|
2023-07-29 14:41:57 +02:00
|
|
|
The application ID is defined as:
|
2022-04-05 10:42:02 +02:00
|
|
|
|
2023-07-29 14:41:57 +02:00
|
|
|
```C
|
|
|
|
#define APPLICATION_ID "com.github.ToshioCP.dnd"
|
|
|
|
```
|
2022-04-05 10:42:02 +02:00
|
|
|
|
2023-07-29 14:41:57 +02:00
|
|
|
### Startup signal handler
|
2022-04-05 10:42:02 +02:00
|
|
|
|
2023-07-29 14:41:57 +02:00
|
|
|
Most of the work is done in the "startup" signal handler.
|
2022-04-05 10:42:02 +02:00
|
|
|
|
2023-07-29 14:41:57 +02:00
|
|
|
Two objects GtkDragSource and GtkDropTarget is used for DND implementation.
|
2022-04-05 10:42:02 +02:00
|
|
|
|
2023-07-29 14:41:57 +02:00
|
|
|
- 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.
|
2021-06-15 04:24:42 +02:00
|
|
|
|
2023-07-29 14:41:57 +02:00
|
|
|
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.
|
2022-04-05 10:42:02 +02:00
|
|
|
|
2023-07-29 14:41:57 +02:00
|
|
|
- [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)
|
2022-04-05 10:42:02 +02:00
|
|
|
|
2023-01-18 06:37:14 +01:00
|
|
|
~~~C
|
2023-07-29 14:41:57 +02:00
|
|
|
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 }
|
2021-06-15 04:24:42 +02:00
|
|
|
~~~
|
|
|
|
|
2023-07-29 14:41:57 +02:00
|
|
|
- 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
|
2023-01-18 06:37:14 +01:00
|
|
|
|
|
|
|
~~~C
|
2023-07-29 14:41:57 +02:00
|
|
|
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 }
|
2022-04-05 10:42:02 +02:00
|
|
|
~~~
|
|
|
|
|
2023-07-29 14:41:57 +02:00
|
|
|
This handler just shows the window.
|
2022-04-05 10:42:02 +02:00
|
|
|
|
2023-07-29 14:41:57 +02:00
|
|
|
### Drop signal handler
|
2021-06-15 04:24:42 +02:00
|
|
|
|
|
|
|
~~~C
|
2023-07-29 14:41:57 +02:00
|
|
|
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 }
|
2021-06-15 04:24:42 +02:00
|
|
|
~~~
|
|
|
|
|
2023-07-29 14:41:57 +02:00
|
|
|
The "drop" signal handler has five parameters.
|
2023-07-15 10:27:53 +02:00
|
|
|
|
2023-07-29 14:41:57 +02:00
|
|
|
- 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.
|
2023-07-15 10:27:53 +02:00
|
|
|
|
2023-07-29 14:41:57 +02:00
|
|
|
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.
|
2023-07-15 10:27:53 +02:00
|
|
|
|
2023-07-29 14:41:57 +02:00
|
|
|
## Meson.build
|
2023-07-15 10:27:53 +02:00
|
|
|
|
2023-07-29 14:41:57 +02:00
|
|
|
The file `meson.build` controls the building process.
|
2023-07-15 10:27:53 +02:00
|
|
|
|
2023-07-29 14:41:57 +02:00
|
|
|
~~~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)
|
2023-07-15 10:27:53 +02:00
|
|
|
~~~
|
|
|
|
|
2023-07-29 14:41:57 +02:00
|
|
|
You can build it from the command line.
|
2021-06-15 04:24:42 +02:00
|
|
|
|
2023-07-29 14:41:57 +02:00
|
|
|
```
|
|
|
|
$ cd src/dnd
|
|
|
|
$ meson setup _build
|
|
|
|
$ ninja -C _build
|
|
|
|
$ _build/dnd
|
|
|
|
```
|
2021-06-15 04:24:42 +02:00
|
|
|
|
2023-07-29 14:41:57 +02:00
|
|
|
The source files are under the directory `src/dnd` of the [repository](https://github.com/ToshioCP/Gtk4-tutorial).
|
|
|
|
Download it and see the directory.
|
2021-06-15 04:24:42 +02:00
|
|
|
|
2023-01-18 06:37:14 +01:00
|
|
|
Up: [README.md](../README.md), Prev: [Section 27](sec27.md), Next: [Section 29](sec29.md)
|