如何在C语言GTK3中为带模型过滤器的多选树视图实现DND功能
GTK树视图多选模式DND的drag-data-get回调实现方案
核心问题说明
你在多选场景下遍历调用gtk_selection_data_set无效的核心原因是:该函数每次调用都会直接覆盖之前设置的所有选择数据,无法增量追加内容,需要先将所有选中行的数据序列化后一次性传入。
具体实现步骤
1. 提前注册自定义MIME类型
在初始化拖放源的时候,提前注册专属的多选行传输MIME类型,避免和其他拖放类型冲突,示例:
// 定义自定义MIME类型 #define TARGET_MULTIPLE_ROWS 0 static const GtkTargetEntry drag_targets[] = { { "application/x-gtk-treeview-multiple-rows", GTK_TARGET_SAME_APP, TARGET_MULTIPLE_ROWS } }; // 给树视图注册拖放源 gtk_drag_source_set(tree_view, GDK_BUTTON1_MASK, drag_targets, G_N_ELEMENTS(drag_targets), GDK_ACTION_COPY | GDK_ACTION_MOVE);
2. 重写drag-data-get回调逻辑
完整回调处理逻辑如下:
static void on_drag_data_get(GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data, guint info, guint time, gpointer user_data) { if (info != TARGET_MULTIPLE_ROWS) return; GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)); GList *selected_rows = gtk_tree_selection_get_selected_rows(selection, NULL); if (!selected_rows) { gtk_selection_data_set(selection_data, gdk_atom_intern("application/x-gtk-treeview-multiple-rows", FALSE), 8, (const guchar*)"", 0); return; } // 序列化所有选中行数据,这里用换行符分隔每个行的路径字符串,也可以替换为需要的业务字段 GString *data_buf = g_string_new(""); for (GList *l = selected_rows; l; l = l->next) { GtkTreePath *path = l->data; gchar *path_str = gtk_tree_path_to_string(path); g_string_append_printf(data_buf, "%s\n", path_str); g_free(path_str); } // 一次性设置选择数据 gtk_selection_data_set(selection_data, gdk_atom_intern("application/x-gtk-treeview-multiple-rows", FALSE), 8, (const guchar*)data_buf->str, data_buf->len ); // 释放内存 g_string_free(data_buf, TRUE); g_list_free_full(selected_rows, (GDestroyNotify)gtk_tree_path_free); }
3. 接收端drag-data-received回调配套处理
在接收拖放的树视图回调中拆分序列化数据即可:
static void on_drag_data_received(GtkWidget *widget, GdkDragContext *context, gint x, gint y, GtkSelectionData *selection_data, guint info, guint time, gpointer user_data) { if (info != TARGET_MULTIPLE_ROWS || gtk_selection_data_get_length(selection_data) <= 0) { gtk_drag_finish(context, FALSE, FALSE, time); return; } gchar **rows = g_strsplit((const gchar*)gtk_selection_data_get_data(selection_data), "\n", -1); for (int i = 0; rows[i] && rows[i][0]; i++) { GtkTreePath *path = gtk_tree_path_new_from_string(rows[i]); // 自定义业务处理逻辑,比如从源模型取数据插入到目标模型 gtk_tree_path_free(path); } g_strfreev(rows); gtk_drag_finish(context, TRUE, FALSE, time); }
注意事项
- 如果需要传输业务数据而非树路径,可以修改序列化逻辑,将每个行需要的字段按自定义规则拼接,接收端对应解析即可
- 若业务数据本身包含换行符,可以替换为其他不会出现的分隔符,或者做转义处理
- 如果是同应用内拖放,也可以直接传GList指针,但需要严格控制内存生命周期,避免野指针,序列化字符串的方案兼容性更高,也支持跨进程拖放
内容的提问来源于stack exchange,提问作者Ben
相关产品推荐
相关产品推荐

