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

GTK3 C语言开发:如何实现GtkTextView文本绕排图片

解决GTK3 GtkTextView中文本环绕图片的问题

首先明确:gtk_text_buffer_insert_pixbuf 插入的图片属于行内嵌入元素,会被当作单个字符处理,自然会拉高整行高度,无法实现文本绕排效果。要实现图片旁侧的文本自动绕排,有两种可行方案:

方案一:利用Pango浮动属性实现原生绕排

Pango 1.20+ 支持 PANGO_ATTR_FLOAT 属性,可将嵌入的元素设置为左/右浮动,让文本自动环绕它。结合GTK3的GtkTextChildAnchor实现:

代码示例

#include <gtk/gtk.h>

int main(int argc, char *argv[]) {
    gtk_init(&argc, &argv);

    GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_default_size(GTK_WINDOW(window), 600, 400);
    g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);

    GtkWidget *text_view = gtk_text_view_new();
    gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text_view), GTK_WRAP_WORD);

    GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view));
    // 插入测试用大段文本
    const gchar *long_text = "这里是大段测试文本,用来测试图片绕排效果。这里是大段测试文本,用来测试图片绕排效果。这里是大段测试文本,用来测试图片绕排效果。这里是大段测试文本,用来测试图片绕排效果。这里是大段测试文本,用来测试图片绕排效果。这里是大段测试文本,用来测试图片绕排效果。这里是大段测试文本,用来测试图片绕排效果。这里是大段测试文本,用来测试图片绕排效果。这里是大段测试文本,用来测试图片绕排效果。这里是大段测试文本,用来测试图片绕排效果。这里是大段测试文本,用来测试图片绕排效果。这里是大段测试文本,用来测试图片绕排效果。这里是大段测试文本,用来测试图片绕排效果。这里是大段测试文本,用来测试图片绕排效果。这里是大段测试文本,用来测试图片绕排效果。这里是大段测试文本,用来测试图片绕排效果。这里是大段测试文本,用来测试图片绕排效果。这里是大段测试文本,用来测试图片绕排效果。这里是大段测试文本,用来测试图片绕排效果。这里是大段测试文本,用来测试图片绕排效果。";
    gtk_text_buffer_insert_at_cursor(buffer, long_text, -1);

    // 创建并设置图片
    GtkWidget *image = gtk_image_new_from_file("test.png"); // 替换为你的图片路径
    gtk_widget_set_size_request(image, 150, 150);

    // 在文本起始位置插入子锚点
    GtkTextIter iter;
    gtk_text_buffer_get_start_iter(buffer, &iter);
    GtkTextChildAnchor *anchor = gtk_text_child_anchor_new();
    gtk_text_buffer_insert_child_anchor(buffer, &iter, anchor);
    gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(text_view), image, anchor);

    // 创建浮动属性标签
    GtkTextTag *float_tag = gtk_text_tag_new("float-image");
    PangoAttribute *float_attr = pango_attr_float_new(PANGO_FLOAT_LEFT);
    gint start_offset = gtk_text_iter_get_offset(&iter) - 1; // 回退到锚点所在位置
    pango_attr_set_start_index(float_attr, start_offset);
    pango_attr_set_end_index(float_attr, start_offset + 1);
    gtk_text_tag_add_attribute(float_tag, PANGO_ATTR_FLOAT, float_attr);
    pango_attribute_destroy(float_attr);

    // 将标签添加到缓冲区并应用到锚点位置
    gtk_text_buffer_get_tag_table(buffer)->add(GTK_TEXT_TAG_TABLE(buffer), float_tag);
    GtkTextIter start_iter, end_iter;
    gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start_offset);
    gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, start_offset + 1);
    gtk_text_buffer_apply_tag(buffer, float_tag, &start_iter, &end_iter);

    gtk_container_add(GTK_CONTAINER(window), text_view);
    gtk_widget_show_all(window);

    gtk_main();
    return 0;
}

优缺点

  • 优点:原生排版支持,窗口大小变化时文本自动重新绕排,无需手动计算。
  • 缺点:依赖Pango 1.20+及对应版本的GTK3,旧版本环境可能不兼容。

方案二:GtkOverlay+动态文本缩进(兼容性更好)

用GtkOverlay将图片悬浮在TextView上方,通过监听TextView的尺寸变化,动态给图片覆盖的文本行设置左缩进,实现文本绕排效果:

代码示例

#include <gtk/gtk.h>

static void adjust_text_margins(GtkWidget *text_view, GdkRectangle *allocation, GtkWidget *image) {
    GtkTextView *tv = GTK_TEXT_VIEW(text_view);
    GtkTextBuffer *buffer = gtk_text_view_get_buffer(tv);

    // 获取图片尺寸
    gint img_width, img_height;
    gtk_widget_get_preferred_size(image, NULL, &img_width, &img_height);

    // 计算单行文本高度
    PangoContext *context = gtk_widget_get_pango_context(text_view);
    PangoFontMetrics *metrics = pango_context_get_metrics(context, NULL, NULL);
    gint line_height = pango_units_to_pixels(pango_font_metrics_get_ascent(metrics) + pango_font_metrics_get_descent(metrics));
    pango_font_metrics_destroy(metrics);

    // 计算图片占据的行数
    gint lines_occupied = img_height / line_height;
    lines_occupied += (img_height % line_height != 0) ? 1 : 0;

    // 创建/获取缩进标签
    GtkTextTag *indent_tag = GTK_TEXT_TAG(gtk_text_buffer_get_tag_table(buffer)->lookup(GTK_TEXT_TAG_TABLE(buffer), "image-indent"));
    if (!indent_tag) {
        indent_tag = gtk_text_tag_new("image-indent");
        gtk_text_buffer_get_tag_table(buffer)->add(GTK_TEXT_TAG_TABLE(buffer), indent_tag);
    }
    gtk_text_tag_set_property(indent_tag, "left-margin", img_width);

    // 移除旧的缩进应用,重新设置
    GtkTextIter start_iter, end_iter;
    gtk_text_buffer_get_start_iter(buffer, &start_iter);
    gtk_text_buffer_remove_tag_by_name(buffer, "image-indent", &start_iter, NULL);
    
    gtk_text_buffer_get_iter_at_line(buffer, &end_iter, lines_occupied);
    gtk_text_buffer_apply_tag(buffer, indent_tag, &start_iter, &end_iter);
}

int main(int argc, char *argv[]) {
    gtk_init(&argc, &argv);

    GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_default_size(GTK_WINDOW(window), 600, 400);
    g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);

    GtkWidget *overlay = gtk_overlay_new();
    gtk_container_add(GTK_CONTAINER(window), overlay);

    GtkWidget *text_view = gtk_text_view_new();
    gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text_view), GTK_WRAP_WORD);
    gtk_container_add(GTK_CONTAINER(overlay), text_view);

    GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view));
    const gchar *long_text = "这里是大段测试文本,用来测试图片绕排效果。这里是大段测试文本,用来测试图片绕排效果。这里是大段测试文本,用来测试图片绕排效果。这里是大段测试文本,用来测试图片绕排效果。这里是大段测试文本,用来测试图片绕排效果。这里是大段测试文本,用来测试图片绕排效果。这里是大段测试文本,用来测试图片绕排效果。这里是大段测试文本,用来测试图片绕排效果。这里是大段测试文本,用来测试图片绕排效果。这里是大段测试文本,用来测试图片绕排效果。这里是大段测试文本,用来测试图片绕排效果。这里是大段测试文本,用来测试图片绕排效果。这里是大段测试文本,用来测试图片绕排效果。这里是大段测试文本,用来测试图片绕排效果。这里是大段测试文本,用来测试图片绕排效果。这里是大段测试文本,用来测试图片绕排效果。这里是大段测试文本,用来测试图片绕排效果。这里是大段测试文本,用来测试图片绕排效果。这里是大段测试文本,用来测试图片绕排效果。这里是大段测试文本,用来测试图片绕排效果。";
    gtk_text_buffer_insert_at_cursor(buffer, long_text, -1);

    // 创建并设置图片
    GtkWidget *image = gtk_image_new_from_file("test.png");
    gtk_widget_set_size_request(image, 150, 150);
    gtk_overlay_add_overlay(GTK_OVERLAY(overlay), image);
    gtk_widget_set_halign(image, GTK_ALIGN_START);
    gtk_widget_set_valign(image, GTK_ALIGN_START);
    // 允许事件透传,确保图片下方的文本可交互
    gtk_overlay_set_overlay_pass_through(GTK_OVERLAY(overlay), image, TRUE);

    // 监听尺寸变化,动态调整缩进
    g_signal_connect(text_view, "size-allocate", G_CALLBACK(adjust_text_margins), image);

    gtk_widget_show_all(window);

    gtk_main();
    return 0;
}

优缺点

  • 优点:兼容性强,不依赖特定版本的Pango/GTK,图片与文本分层管理,交互逻辑更灵活。
  • 缺点:需要手动计算行数和缩进,文本内容或窗口尺寸变化时需重新计算,逻辑稍复杂。

内容的提问来源于stack exchange,提问作者Harshith

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 09:37:11