文本小组件概述 [src]
GTK 有一个用于多行文本编辑极其强大的框架。参与进程的主要对象有 GtkTextBuffer
(表示所编辑的文本)和 GtkTextView
(可以显示 GtkTextBuffer
的小组件)。每个缓冲区都可以由任意数量的视图显示。
在 GTK 中关于文本需要记住的一件重要事情是,它采用 UTF-8 编码。这意味着一个字符可以用多个字节编码。字符计数通常称为“偏移量”,而字节计数称为“索引”。如果将这两个混淆,使用 ASCII 时没有问题,但缓冲区中一旦包含多字节字符便会出问题。
缓冲区中的文本可以用“标记”标记。标记是可以应用于某个文本范围的属性。例如,一个标记可以称为“粗体”,使标记内的文本加粗。但是,标记概念比这更普遍;标记不必影响外观。它们还可以影响鼠标和按键的行为,“锁定”一段文本以防用户编辑或无数其他操作。标记由 GtkTextTag
对象表示。一个 GtkTextTag
可以应用于任何数量缓冲区内的任意数量文本范围。
每个标记都存储在 GtkTextTagTable
中。标记表定义了一组可一起使用的标记。每个缓冲区都有一个与之关联的标记表;只有来自该标记表的标记可与缓冲区一起使用。但是,单个标记表可以在多个缓冲区之间共享。
标记可以有名称,有时会很方便(例如,您可以将使内容加粗的标记命名为“加粗”),但它们也可以是匿名的(如果您在即时创建标记时会很方便)。
大多数文本操作可通过迭代器完成,由 GtkTextIter
表示。迭代器表示文本缓冲区中两个字符之间的位置。GtkTextIter
是一个旨在在堆栈上分配的结构;它保证可以通过值进行复制,并且从不包含任何堆分配数据。迭代器并不是无限有效的;每当以影响缓冲区中字符数的方式修改缓冲区时,所有未完成的迭代器都将失效。(请注意,删除 5 个字符然后重新插入 5 个字符仍然会使迭代器无效,尽管最终字符数与您传递不同状态之间的字符数相同)。
因此,不能使用迭代器在缓冲区修改之间保留位置。要保留位置,GtkTextMark
对象是理想选择。您可以将标记视为不可见的游标或插入点;它浮动在缓冲区中,保存一个位置。如果删除标记周围的文本,则标记将保留在文本曾经占据的位置;如果在标记处插入文本,则根据标记的权重,标记最终位于新文本的左侧或右侧。从左到右语言中的标准文本游标是具有右权重的标记,因为它位于已插入文本的右侧。
与标记类似,标记可以是命名或匿名的。GtkTextBuffer
中内置了两个标记;它们分别名为“插入”和“selection_bound”,并分别指代插入点和选择区域的边界(而不是插入点)。如果没有选择文本,这两个标记将处于相同位置。通过移动这些标记,您可以操作所选内容和光标出现的位置。
如果您想在响应用户操作时放置光标,请确保使用 gtk_text_buffer_place_cursor(),这会一次移动两者,而不会导致暂时选择(先移动一个然后移动另一个会暂时选择旧位置和新位置之间的范围)。
文本缓冲区始终至少包含一行,但可能为空(即,缓冲区可以不包含一个字符)。文本缓冲区中的最后一行永远不会以行分隔符(如换行符)结尾;缓冲区中的其他行总是以行分隔符结尾。计算字符计数和字符偏移量时,行分隔符作为字符进行计数。请注意,某些 Unicode 行分隔符在 UTF-8 中用多字节表示,而两个字符序列“\r\n”也视为行分隔符。
如果 gtk_text_buffer_set_enable_undo()
已设置为 TRUE
,则文本缓冲区支持撤消和恢复。使用 gtk_text_buffer_undo()
或 gtk_text_buffer_redo()
执行必要的操作。请注意,如果缓冲区不可编辑,则会忽略这些操作。开发人员可能希望某些操作不可撤销。要执行此操作,请将您的更改包装在 gtk_text_buffer_begin_irreversible_action()
和 gtk_text_buffer_end_irreversible_action() 中。
简单示例
GtkTextView
最简单的用法可能如下
GtkWidget *view;
GtkTextBuffer *buffer;
view = gtk_text_view_new ();
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
gtk_text_buffer_set_text (buffer, "Hello, this is some text", -1);
/* Now you might put the view in a container and display it on the
* screen; when the user edits the text, signals on the buffer
* will be emitted, such as "changed", "insert_text", and so on.
*/
在很多情况下,也可以先使用 gtk_text_buffer_new() 创建缓冲区,然后使用 gtk_text_view_new_with_buffer() 为该缓冲区创建一个窗口小部件。或者,您可以在创建窗口小部件后使用 gtk_text_view_set_buffer() 更改窗口小部件显示的缓冲区。
更改文本属性的示例
在 GtkTextView
中影响文本属性的方法是应用标签,这些标签会更改文本区域的属性。对于来自主题的文本功能,例如字体和前景色,请使用 CSS 覆盖它们的默认值。
GtkWidget *view;
GtkTextBuffer *buffer;
GtkTextIter start, end;
PangoFontDescription *font_desc;
GdkRGBA rgba;
GtkTextTag *tag;
GtkCssProvider *provider;
GtkStyleContext *context;
view = gtk_text_view_new ();
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
gtk_text_buffer_set_text (buffer, "Hello, this is some text", -1);
/* Change default font and color throughout the widget */
provider = gtk_css_provider_new ();
gtk_css_provider_load_from_data (provider,
"textview {"
" font: 15px serif;"
" color: green;"
"}",
-1);
context = gtk_widget_get_style_context (view);
gtk_style_context_add_provider (context,
GTK_STYLE_PROVIDER (provider),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
/* Change left margin throughout the widget */
gtk_text_view_set_left_margin (GTK_TEXT_VIEW (view), 30);
/* Use a tag to change the color for just one part of the widget */
tag = gtk_text_buffer_create_tag (buffer, "blue_foreground",
"foreground", "blue",
NULL);
gtk_text_buffer_get_iter_at_offset (buffer, &start, 7);
gtk_text_buffer_get_iter_at_offset (buffer, &end, 12);
gtk_text_buffer_apply_tag (buffer, tag, &start, &end);
随 GTK 提供的 gtk4-demo
应用程序包含更多 GtkTextView
示例代码。