forked from AuroraMiddleware/gtk
158 lines
7.1 KiB
Markdown
158 lines
7.1 KiB
Markdown
# Text Widget Overview {#TextWidget}
|
|
|
|
GTK has an extremely powerful framework for multiline text editing. The
|
|
primary objects involved in the process are #GtkTextBuffer, which represents the
|
|
text being edited, and #GtkTextView, a widget which can display a #GtkTextBuffer.
|
|
Each buffer can be displayed by any number of views.
|
|
|
|
One of the important things to remember about text in GTK is that it's in
|
|
the UTF-8 encoding. This means that one character can be encoded as multiple
|
|
bytes. Character counts are usually referred to as _offsets_, while byte
|
|
counts are called _indexes_. If you confuse these two, things will work fine
|
|
with ASCII, but as soon as your buffer contains multibyte characters, bad
|
|
things will happen.
|
|
|
|
Text in a buffer can be marked with _tags_. A tag is an attribute that can
|
|
be applied to some range of text. For example, a tag might be called "bold"
|
|
and make the text inside the tag bold. However, the tag concept is more
|
|
general than that; tags don't have to affect appearance. They can instead
|
|
affect the behavior of mouse and key presses, "lock" a range of text so the
|
|
user can't edit it, or countless other things. A tag is represented by a
|
|
#GtkTextTag object. One #GtkTextTag can be applied to any number of text
|
|
ranges in any number of buffers.
|
|
|
|
Each tag is stored in a #GtkTextTagTable. A tag table defines a set of
|
|
tags that can be used together. Each buffer has one tag table associated with
|
|
it; only tags from that tag table can be used with the buffer. A single tag
|
|
table can be shared between multiple buffers, however.
|
|
|
|
Tags can have names, which is convenient sometimes (for example, you can name
|
|
your tag that makes things bold "bold"), but they can also be anonymous (which
|
|
is convenient if you're creating tags on-the-fly).
|
|
|
|
Most text manipulation is accomplished with _iterators_, represented by a
|
|
#GtkTextIter. An iterator represents a position between two characters in
|
|
the text buffer. #GtkTextIter is a struct designed to be allocated on the
|
|
stack; it's guaranteed to be copiable by value and never contain any
|
|
heap-allocated data. Iterators are not valid indefinitely; whenever the
|
|
buffer is modified in a way that affects the number of characters in the
|
|
buffer, all outstanding iterators become invalid. (Note that deleting 5
|
|
characters and then reinserting 5 still invalidates iterators, though you
|
|
end up with the same number of characters you pass through a state with a
|
|
different number).
|
|
|
|
Because of this, iterators can't be used to preserve positions across buffer
|
|
modifications. To preserve a position, the #GtkTextMark object is ideal. You
|
|
can think of a mark as an invisible cursor or insertion point; it floats in
|
|
the buffer, saving a position. If the text surrounding the mark is deleted,
|
|
the mark remains in the position the text once occupied; if text is inserted
|
|
at the mark, the mark ends up either to the left or to the right of the new
|
|
text, depending on its _gravity_. The standard text cursor in left-to-right
|
|
languages is a mark with right gravity, because it stays to the right of
|
|
inserted text.
|
|
|
|
Like tags, marks can be either named or anonymous. There are two marks
|
|
built-in to #GtkTextBuffer; these are named "insert" and "selection_bound"
|
|
and refer to the insertion point and the boundary of the selection which
|
|
is not the insertion point, respectively. If no text is selected, these
|
|
two marks will be in the same position. You can manipulate what is selected
|
|
and where the cursor appears by moving these marks around.
|
|
|
|
If you want to place the cursor in response to a user action, be sure to use
|
|
gtk_text_buffer_place_cursor(), which moves both at once without causing a
|
|
temporary selection (moving one then the other temporarily selects the range in
|
|
between the old and new positions).
|
|
|
|
Text buffers always contain at least one line, but may be empty (that
|
|
is, buffers can contain zero characters). The last line in the text
|
|
buffer never ends in a line separator (such as newline); the other
|
|
lines in the buffer always end in a line separator. Line separators
|
|
count as characters when computing character counts and character
|
|
offsets. Note that some Unicode line separators are represented with
|
|
multiple bytes in UTF-8, and the two-character sequence "\r\n" is also
|
|
considered a line separator.
|
|
|
|
Text buffers support undo and redo if gtk_text_buffer_set_enable_undo()
|
|
has been set to %TRUE. Use gtk_text_buffer_undo() or gtk_text_buffer_redo()
|
|
to perform the necessary action. Note that these operations are ignored if
|
|
the buffer is not editable. Developers may want some operations to not be
|
|
undoable. To do this, wrap your changes in
|
|
gtk_text_buffer_begin_irreversible_action() and
|
|
gtk_text_buffer_end_irreversible_action().
|
|
|
|
## Simple Example
|
|
|
|
The simplest usage of #GtkTextView might look like this:
|
|
|
|
``` {.c}
|
|
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.
|
|
*/
|
|
```
|
|
|
|
In many cases it's also convenient to first create the buffer with
|
|
gtk_text_buffer_new(), then create a widget for that buffer with
|
|
gtk_text_view_new_with_buffer(). Or you can change the buffer the widget
|
|
displays after the widget is created with gtk_text_view_set_buffer().
|
|
|
|
## Example of Changing Text Attributes
|
|
|
|
The way to affect text attributes in #GtkTextView is to
|
|
apply tags that change the attributes for a region of text.
|
|
For text features that come from the theme — such as font and
|
|
foreground color -- use CSS to override their default values.
|
|
|
|
```
|
|
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: 15 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);
|
|
```
|
|
|
|
The `gtk4-demo` application that comes with
|
|
GTK contains more example code for #GtkTextView.
|