You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 19:15:05