複数行テキスト表示ウィジェットの概要 - Gtk::TextView、Gtk::TextBuffer、およびその関連クラスについて

※ このページは、GTK+ 2 Reference Manual: Text Widget Overviewを翻訳し、Ruby-GNOME2向けに書き換えたものです。一部、訳者が補足した内容がありますが、その部分は背景色を変えてあります。

このページの目次

コンセプト

ウィジェットを構成する主なクラス
文字エンコーディングに関する注意
TextViewでのテキストの振る舞いをコントロールする方法
テキストデータ上の"位置"を管理する方法
TextBufferでの行の扱い

シンプルなサンプル
テキスト属性変更のサンプル
参考リンク
このページの更新履歴

コンセプト

ウィジェットを構成する主なクラス

 GTK+には極めてパワフルな複数行テキスト編集の機能を持つ仕組みが用意されています。その中で中心的な役割を果たしているのが、編集されるテキストそのものを表すGtk::TextBufferクラス、および編集テキストを表示するためのGtk::TextViewクラスです。

これらのクラスを使うことで、例えば、同じテキスト(TextBuffer)を同時に複数のユーザインターフェース(TextView)で表示・編集できたりします。

文字エンコーディングに関する注意

 GTK+でテキストを扱う際に覚えておいた方が良いのは、文字コードがUTF-8エンコーディングであるものとして扱われるということです。これは1文字が複数バイトで表現される場合があることを意味します。

GTK+では通常、文字を数える単位としてオフセット(offset)、バイト数を数える単位としてインデックス(index)という言葉が使われるのですが、この2つの単位を混同すると、ASCIIコードの文字のみを含むテキストを扱う場合はともかく、複数バイトで表現される文字が混ざった途端に不具合が起きます。

TextViewでのテキストの振る舞いをコントロールする方法

 TextBufferで管理するテキストには、Gtk::TextTagオブジェクトを貼り付けることがてきます。TextTagとはテキストの特定の範囲を指定して設定することができる"属性"を表します。

例えば、"bold"という種類のTextTagを貼り付けると、その部分のテキストをbold(太字)にして表示することができます。

ただし、TextTagはテキストの見栄えをコントロールするためだけのものではありません。TextTagを貼り付けることによって、マウスやキーの入力に対する振る舞いを変更したり、テキストを編集できないようにロックしたり、いろいろなことができます。

1つのTextTagオブジェクトは、同時に複数のTextBufferの複数のテキスト箇所に対して使うことができます。

 テキストにつけられるTextTagはすべてGtk::TextTagTableオブジェクトに格納されます。TextTagTableは特定のTextBufferで使えるTextTagの集合を表し、TextBufferはTextTagTableを1つだけ持つことができます。TextBufferが管理するテキストには、そのTextTagTableに登録されているTextTagだけしか貼り付けることができません。

ただし、1つのTextTagTableを複数のTextBufferに持たせることができます。

 TextTagには利便性のために名前を付けることができます(例えば、テキストを太く表示するためのTextTagに"bold"と名付けるなど)が、動的に生成して使う時など面倒な場合にはつけなくてもかまいません。

 上記のTextTagを利用する方法とは別に、Gtk::TextView#default_attributesメソッドによって取得できるGtk::TextAttributesオブジェクトを操作することで、TextTagを貼り付けていないテキスト全体に適用されるデフォルトの属性を設定することもできます。

TextViewにはデフォルトの属性を直接変更できるメソッドがいくつかありますが、これらはGtk::TextAttributesオブジェクトを経由する手間を減らすためのショートカットです。

テキストデータ上の"位置"を管理する方法

 ほとんどのテキスト操作処理には、Gtk::TextIterオブジェクトが必要です。TextIterはTextBuffer内にあるテキストの文字と文字の間の位置を表しています。

 (Rubyから利用している元のライブラリの)GtkTextIterはスタックに格納されるように設計されており、値のコピーが可能で、ヒープに格納されるデータを含まないことが保証されています。(訳注:この部分は訳者の知識不足のため、意味がよくわかりません)

 TextIterはTextBuffer内のテキストが変更された時点で値が破棄されます。テキストの変更をまたいで位置情報を保持したい場合は、Gtk::TextMarkオブジェクトを利用してください。

TextMarkは見えないカーソルあるいは挿入ポイントとしてイメージすると分かりやすいです。TextMarkはテキストが変更されるごとに、必要があれば自動で移動し、位置情報が更新されます。

例えば、TextMarkを挟んで存在しているテキストが削除されると、そのテキストがあった位置にTextMarkだけが残ります。あるいはTextMarkがある位置に新しいテキストが挿入された場合、TextMarkは挿入されたテキストの左側または右側に移動します。どちら側に移動するかは"left-gravity"プロパティの値によって決まります。横書きのテキストが左から右に向かって描画される言語では、TextMarkによって位置が管理されている標準のカーソルはテキストが挿入されると右に移動しますが、この時"left-gravity"プロパティはfalseになっています。

 TextMarkもまた、名前はつけてもつけなくてもかまいません。TextBufferには、最初から組み込みのTextMarkが2つ用意されており、"insert"と"selection_bound"という名前がつけられています。それぞれ、「カーソル(挿入ポイント)位置」と「(カーソル位置から続く)選択範囲の終了位置」を表しています。テキストが選択されていない状態の時には、2つのTextMarkの位置情報は同じになります。

この2つのTextMarkを操作することによって、カーソル位置や選択範囲の終了位置を自由に変更することができます。(もし選択範囲の変更が見えてしまうのがいやなら、Gtk::TextBuffer#place_cursorメソッドを使うという手もあります)

TextBufferでの行の扱い

 TextBufferでは、たとえテキストが1文字もない状態であっても、最低1つは行が存在しているとみなされます。またテキストが1文字以上存在し、その末尾が行区切り(改行)だった場合、その後ろに長さが0の行があるとみなされます。

従ってTextBufferでは、常に最後の行には行区切り(改行)は存在せず、(もしあれば)その他の行は必ず行区切り(改行)で終わることになります。

 TextBufferにおいて、行区切り(改行)は、文字数を数えたり文字の位置を調べる際には文字としてカウントされます。UTF-8エンコーディングの場合、一部のユニコードの行区切り(改行)は複数バイトで表現されるので注意が必要です。また"\r\n"という文字列が行区切り(改行)として扱われることにも注意してください。

シンプルなサンプル

 一番シンプルなGtk::TextViewの使い方はこんな感じです。

# coding: utf-8
require 'gtk2'
#require 'gtk3'

w = Gtk::Window.new
w.signal_connect( "destroy" ) { Gtk.main_quit }
s = Gtk::ScrolledWindow.new
v = Gtk::TextView.new

w.add(s)
s.add(v)
v.buffer.text = "これはTextBufferのテキストです。"

w.show_all
Gtk::main
※ このコードはUTF-8エンコーディングで保存して実行してください。このコードに限ってはgtk2、gtk3どちらでも動きます。

 上のサンプルでは、デフォルトで作成されるTextBufferを使っていますが、TextBufferをまず先に作っておいて、それを引数に指定してGtk::TextView.newメソッドを実行することもできます。またどちらにしても、後でGtk::TextView#buffer=メソッドまたはGtk::TextView#set_bufferメソッドを実行することで、表示するTextBufferを変更することができます。

テキスト属性変更のサンプル

 既に書いたように、TextViewに表示するテキストの属性を変更する方法は、TextViewのデフォルトの属性を設定するものとTextTagを利用するものの2つがあります。

これらはTextViewおよびその関連クラス独自の機能を利用するやり方ですが、それとは別に、Gtk::Widgetおよびこれを継承したクラスが持つ、Gtk::Widget#modify_fontメソッドやGtk::Widget#modify_textメソッドを使って、ウィジェットが持つ属性を操作するという形で、フォントやテキストの色を変更することもできます。

# coding: utf-8
require 'gtk2'
#require 'gtk3'

w = Gtk::Window.new
s = Gtk::ScrolledWindow.new
v = Gtk::TextView.new

w.set_size_request( 400, 100 )
w.signal_connect( "destroy" ) { Gtk.main_quit }
w.add(s)
s.add(v)
v.buffer.text = "これはTextBufferのテキストです。"

v.modify_font( Pango::FontDescription.new( "meiryo, 16" ) )
v.modify_text( Gtk::STATE_NORMAL, Gdk::Color.parse( "green" ) )
# for gtk3
#v.override_font( Pango::FontDescription.new( "meiryo, 16" ) )
#v.override_color( Gtk::StateFlags::NORMAL, Gdk::RGBA.parse( "green" ) )

tag = v.buffer.create_tag( 'fg_blue', 'foreground' => 'blue' )

from = v.buffer.get_iter_at_offset( 3 )
to = v.buffer.get_iter_at_offset( 13 )
# for gtk3
#from = v.buffer.get_iter_at( :offset => 3 )
#to = v.buffer.get_iter_at( :offset => 13 )

v.buffer.apply_tag( tag, from, to )

v.left_margin = 10

w.show_all
Gtk::main
※ このコードはUTF-8エンコーディングで保存して実行してください。

 ここに挙げた以外に、gtk2またはgtk3に添付されているgtk-demoアプリケーションにも参考になるコード(textview.rb)が含まれています。

参考リンク

 Ruby-GNOME2 Project Website (公式Wiki)

このページの更新履歴

2014/05/19

 公開


Copyright (c) 2005-2014 The GNOME Project