Help us understand the problem. What is going on with this article?

IMEを使う(Windows: TSF編)

Text Services Framework (TSF) とは

TSFはIMEとアプリケーションの間の層で、IMMの後継のようなものです。
IMMと大きく異なるのは、未確定文字列の描画の義務がアプリケーションにあるということです。
(IMMではIMMにいい感じに未確定文字列を表示してもらうことが可能でした)

TSFに対応したアプリケーションでは以下の内容を実装するこのになります。

  • TSF-awareなアプリケーションだという宣言
  • IMEからのテキストの取得
  • IMEからのテキストの変更
  • IMEからの選択範囲の取得
  • IMEからの選択範囲の変更
  • テキストの範囲からディスプレイ上の座標範囲の取得
  • ディスプレイ上の座標からテキスト座標の取得
  • IME側にテキスト、選択範囲、レイアウトの変更等を通知する

コード的に言えば、以下のような内容にあたります。

  • ITextStoreACPを継承したクラスの用意
    • ACP = Application Character Positionです
      • 編集中の文字列のインデックスだと思えば大丈夫です
      • インデックスベースでテキストを編集するインタフェースということです
    • ITextStoreACP2も同様のインタフェースです
      • こちらはWindows Store Appsでも使用可能です
    • ITextStoreAnchorというより抽象度の高いインタフェースもある
      • これは通常のアプリケーションには解放されていないようです(残念無念)
  • ITextStoreACPSinkへの通知の実装
    • テキストの変更で呼ぶ
    • 選択範囲の変更で呼ぶ
    • レイアウトの変更で呼ぶ
    • テキストの状態・属性・ロック変更で呼ぶ
  • 色々な初期化
    • COM周りの処理が大変

ITextStoreACPの実装

ここがTSF対応の要になります。
基本的に以下の変数を管理するものです。

  • 編集中の文字列
  • 選択中の文字列のインデックス

TSFではIMMとは違って、未確定文字列と編集中の文字列の間に大きな扱いの違いはありません。
積極的に区別しない限り、同一のものとして扱うことになります。
(TATEditorでは編集履歴の都合上、積極的に区別している)

IMMでは確定された文字列を受け取って現在の選択範囲に挿入するだけでもよかった処理も、TSFでは未確定文字列の時点からテキストへ挿入して随時編集可能にする必要があります。
より具体的なIMEからの要求を並べるとだいたい以下のようなものになります。

  1. IME側が現在の選択範囲を取得する
  2. IME側が現在の選択範囲を未確定文字列で置換する
    • IME側からは未確定文字列もテキストの一部として見える状態になる
  3. IME側が現在の未確定文字列のディスプレイ上での描画位置を取得
    • 未確定文字列の周辺に変換候補や予測変換が出せる
  4. 未確定文字列をテキスト上で編集していく
    • このとき未確定文字列の装飾情報はアプリケーション側から取得しにいく必要がある
  5. 未確定文字列を確定する

さてこうしたことに対応するために、ITextStoreACPで主に実装しなければいけない関数が次の通りです。

  • GetEndACP
    • 編集中の文字列の長さを返します
  • GetSelection
    • 現在選択中の文字列の始点と終点のACPを返します
    • 複数の選択に対応しており、TATEditorでも複数の選択を返せるのですが、要求された記憶がありません(誰か複数選択に対応したIMEを作りましょう)
  • SetSelection
    • IME側からテキストが選択されます
  • GetText
    • 指定された範囲の文字列を返します
  • SetText
    • 指定された範囲の文字列を指定された文字列で置換します
  • GetACPFromPoint
    • ディスプレイ上の座標に対応するACP(文字列のインデックス)を返します
  • GetTextExt
    • 指定されたテキスト範囲がディスプレイ上のどこにあるかを返します

このほかにも、テキストの編集ロックや文字列の属性を返す関数などがあります。
IMMに比べると実装の量が段違いで増えています。
もちろん、これに対応するだけの価値は十分あります。例えば、

  • 選択中のテキストの再変換
    • MS-IMEだとデフォルトでスペースキーを押せば再変換ができます(便利)
  • IMEが前後の文脈に沿った変換を提供可能
    • もちろんこれはIMEが自由にテキストにアクセスできるからです
    • ちなみに、256文字ずつ全テキストを取得しようとして重くなるIMEとかも存在します
  • より高度なテキスト操作もIMEから可能ですが、あまりフル活用している例は見ませんね
    • tsf-vimとかは面白そうでした
    • 私には日本語入力の知識がないので誰か教えて欲しい

ITextStoreACPSinkへの対応

IME以外からのテキストの変更、選択範囲の変更、レイアウトの変更(文字列の配置やウィンドウの位置の変更など)時に呼びます。
特筆すべき点はありません。

初期化

初期化の手順がかなり面倒で、CoCreateInstance(CLSID_TF_ThreadMgr, NULL, CLSCTX_INPROC_SERVER, IID_ITfThreadMgr, (void**)&pThreadMgr)とかから始めるのですがまとめる時間がなかったのでまたの機会に……。
基本は https://msdn.microsoft.com/library/ms629043.aspx です。

その他

さらに、細かい挙動に対応するためにはより多くのインタフェースを実装する必要があります。
例えば現時点のTATEditorでは以下に対応しています。

  • ITfContextOwnerCompositionSink
    • 未確定文字列の状態の更新を通知してくれます
    • このタイミングで未確定文字列の装飾情報を取得しています

詳しくはReferenceをどうぞ。

おわりに

いつものように中途半端な内容となってしまい申し訳在りません。
明日はまだまだ続くよWindowsのIME API!Windows.UI.Text.Core編です。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした