Tutorial
|
|
|
|
Text Widget
Inti has an extremely powerful
framework for multiline text editing. The primary objects involved in
the process are TextBuffer, which represents the text
being edited, and TextView, a widget which can display
a
TextBuffer. Each buffer can be displayed by any number of views.
One of the important things to remember about text in Inti 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 TextTag object. One TextTag can be
applied to any number of text ranges in any number of buffers.
Each tag is stored in a TextTagTable. 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
TextIter.
An iterator represents a position between two characters in the text
buffer. TextIter is a class designed to be allocated on the stack; it's
guaranteed to be copyable 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 TextMark
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 TextBuffer; 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::TextBuffer::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.
Simple Example
The simplest usage of Gtk::TextView might look like this:
Gtk::TextView *view = new Gtk::TextView;
Gtk::TextBuffer *buffer = view->get_buffer();
buffer->set_text("Hello, this is some text");
// 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. |
Example of Changing Text Attributes
There are two ways to affect text attributes in TextView. You can
change the default attributes for a given TextView, and you can 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
standard Gtk::Widget methods such as modify_font() or modify_text().
For
other attributes there are dedicated methods on TextView such as
set_tabs();
The header file for
TextView Example is textview.h
#include <inti/main.h>
#include <inti/gtk/window.h>
using namespace Inti;
class TextViewWindow
: public Gtk::Window
{
public:
TextViewWindow();
virtual
~TextViewWindow();
}; |
and the source file is textview.cc
#include "textview.h"
#include <inti/gtk/textview.h>
#include <inti/gdk/color.h>
TextViewWindow::TextViewWindow()
{
set_title("TextView Example");
// Create
text view widget
Gtk::TextView *view = new Gtk::TextView;
add(*view);
// Get a
pointer to the default buffer
Gtk::TextBuffer *buffer = view->get_buffer ();
buffer->set_text ("Hello, this is some text");
// Change
default font throughout the widget
Pointer<Pango::FontDescription> font_desc = new
Pango::FontDescription("Serif
15");
view->modify_font(*font_desc);
// Change
default color throughout the widget
Gdk::Color color("green");
view->modify_text(Gtk::STATE_NORMAL, color);
// Change left
margin throughout the widget
view->set_left_margin(30);
// Use a
tag to change the color for just one part of the widget
Gtk::TextTag *tag =
buffer->create_tag("blue_foreground", "foreground", "blue", 0);
Gtk::TextIter start =
buffer->get_iter_at_offset(7);
Gtk::TextIter end =
buffer->get_iter_at_offset(12);
buffer->apply_tag(*tag, start, end);
view->show();
}
TextViewWindow::~TextViewWindow()
{
}
int main (int argc, char *argv[])
{
using namespace
Main;
init(&argc, &argv);
TextViewWindow window;
window.sig_destroy().connect(slot(&Inti::Main::quit));
window.show();
run();
return
0;
} |
The inti-demo application that comes with Inti contains a more detailed
example of code for TextView, in "demos/inti-demo/textview.cc".