改行付きの文字列を描画する - Rustで作るWindowsアプリ
目次
改行付きの文字列を描画する
TextOutW
関数は実は改行を処理できません。改行を入れても表示で改行されないので困ったもんです。
そこで登場するのがDrawTextW
関数です。この関数は改行を処理して表示上でもちゃんと文字列を改行してくれます。
また指定した矩形領域の範囲で自動で折り返してくれますので使い勝手がいいです。
ここではDrawTextW
関数の使い方について解説します。
WM_PAINTの編集
ウィンドウプロシージャ関数内のWM_PAINT
以下を次のように編集します。
WM_PAINT => { let hdc: HDC = GetDC(window); let mut rect: RECT = RECT { left: 10, top: 10, right: 200, bottom: 200, }; // 改行付きの文字列の描画 let s = String::from("こんにちは。\nごきげんいかがですか?私は元気です。"); let mut v: Vec<u16> = s.encode_utf16().collect(); // v.push(0); // ナル文字をプッシュしない DrawTextW(hdc, &mut v, &mut rect, DT_LEFT | DT_WORDBREAK); ReleaseDC(window, hdc); LRESULT(0) }
上記の編集内容でプログラムを実行すると以下のような表示になります。
テキストの改行が処理されまたテキストが矩形の範囲で折り返されてるのがわかります。この関数の注意点としては渡すテキストはミュータブルにしないといけないことです。またテキストの描画位置と描画範囲はRECT
で指定しますがこれもミュータブルにする必要があります。
C言語のDrawTextW
関数では文字列の長さも指定しないといけませんがwindows-rs
のDrawTextW
関数は内部で暗黙的に文字列の長さをAPIに渡してくれてます。
通常、APIに渡す文字列はナル文字が付加されている必要がありますが、ここでは文字ベクタにナル文字をプッシュしていません。これはDrawTextW
関数が「配列の長さ」でテキストを描画するため、ナル文字が含まれているとナル文字も描画してしまうからです。
横道: PCWSTR.as_wideメソッドの動作
なぜTextOutW
関数でPCWSTR
のas_wide
メソッドを使った時はナル文字が描画されなかったのかと言うと、PCWSTR
などのas_wide
メソッドは内部でC系のwcslen
関数で得た文字列の長さでポインタをスライスしているからです。
PCWSTR.as_wide
メソッドの挙動は以下のコードで実験できます。
use windows::{ core::* }; fn main() { let s: PCWSTR = w!("こんにちは"); unsafe { println!("{}", s.display()); // こんにちは println!("{}", s.as_wide().len()); // 5 } }
w!
マクロはナル文字付のu16
の配列を生成し、そのポインタからPCWSTR
構造体を生成します。つまりPCWSTR
はポインタを保持するだけなのでこのw!
マクロで生成されたポインタにはナル文字が含まれています。しかしas_wide
メソッドを使うとそのポインタがスライスされて上記の例では長さが5
の配列になりC言語の文字列の長さ、つまりwcslen
関数などの返り値と一致するようになります。
つまりPCWSTR
などはナル文字を含むポインタを保持するべきで、u16
の配列(ポインタではなく配列)を渡す関数などではナル文字は除去する必要がある、ということになりそうです。
この本で後述するSetWindowTextW
関数などはPCWSTR
を引数に指定しますので、このPCWSTR
にはナル文字が含まれていないといけません。ややこしいですが注意が必要です。
DrawTextW関数
Function windows::Win32::Graphics::Gdi::DrawTextW pub unsafe fn DrawTextW<P0>( hdc: P0, // デバイス コンテキスト lpchtext: &mut [u16], // テキスト(文字配列)へのポインタ lprc: *mut RECT, // 整形用の四角形へのポインタ format: DRAW_TEXT_FORMAT // テキストの整形方法 ) -> i32 // 成功したらテキストの高さ、失敗した場合は`0` where P0: IntoParam<HDC>,
この関数は引数のテキスト(lpchtext
)をデバイス コンテキストに描画します。四角形(lprc
)の範囲でテキストを描画し、書式(format
)に従ってテキストの表示を整えます。
またこの関数は指定の四角形の範囲にテキストをクリップしますがイタリック体などで文字が斜めになっている場合は斜めになってる部分が切り取られる可能性もあります。
指定されたフォントが四角形に対して大きすぎる場合、この関数は小さいフォントに自動で置き換えるというようなことはしません。
返り値は関数が成功した場合はテキストの高さになります。DT_VCENTER
またはDT_BOTTOM
が指定されている場合、返り値は描画されたテキストの下部からのオフセットになります。
DRAW_TEXT_FORMAT構造体
Struct windows::Win32::Graphics::Gdi::DRAW_TEXT_FORMAT #[repr(transparent)] pub struct DRAW_TEXT_FORMAT(pub u32);
この構造体はテキストの書式を指定するためのu32
のラッパーでタプル構造体です。トレイトでビット演算子を実装しており複数の定数をOR演算子で指定することができます。
指定できる定数は以下になります。
- DT_LEFT ... テキストを左揃えにする
- DT_RIGHT ... テキストを右揃えにする
- DT_TOP ... テキストを四角形の上部に揃える
- DT_BOTTOM ... テキストを四角形の下部に揃える。DT_SINGLELINE 値でのみ使用される
- DT_CENTER ... テキストを四角形の水平方向の中央に配置する
- DT_VCENTER ... テキストを四角形の垂直方向に中央揃えにする
- DT_WORDBREAK ... 改行するときに行を単語間で分割する
- DT_WORD_ELLIPSIS ... 四角形に収まらない単語を切り捨て省略記号を付加する
- DT_EXPANDTABS ... タブ文字を展開する。デフォルトの文字数は8
- DT_SINGLELINE ... テキストを1行だけで表示する
- DT_TABSTOP ... タブストップを設定する
これらの定数は一部です。