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
相关产品推荐
相关产品推荐

