「.NETプラグイン開発 イベント編」の版間の差分

提供: MeryWiki
ナビゲーションに移動 検索に移動
MSY-07 (トーク | 投稿記録)
<source>タグを<syntaxhighlight>タグに置き換え
MSY-07 (トーク | 投稿記録)
改行の修正
 
(同じ利用者による、間の1版が非表示)
3行目: 3行目:


== テキスト変更イベント ==
== テキスト変更イベント ==
プラグインの基本は、発生するイベントに応じて処理していくことです。<br>
プラグインの基本は、発生するイベントに応じて処理していくことです。まずは使うことの多いであろう、テキスト変更イベントを処理してみます。
まずは使うことの多いであろう、テキスト変更イベントを処理してみます。<br>
 
<br>
ソースの中で「#region OnEvents のディスパッチ」が閉じられていると思いますので、これを開きます。基本的に対応しているイベントは全て書かれていますので、コメントを外すことで各イベント発生時に呼ばれるようになります。なおコメントアウトしておけば無駄に呼ばれることがないため、遅くしないためにも無駄にコメントアウトは外さないようにしましょう。
ソースの中で「#region OnEvents のディスパッチ」が閉じられていると思いますので、これを開きます。<br>
 
基本的に対応しているイベントは全て書かれていますので、コメントを外すことで各イベント発生時に呼ばれるようになります。<br>
イベントの OnChanged のコメントアウトを外しましょう。これがテキストが変更されたときに呼ばれるイベントです。ここに、時折要望として挙がる「テキスト全体の文字数をステータスバーに表示する」処理を書いてみましょう。
なおコメントアウトしておけば無駄に呼ばれることがないため、遅くしないためにも無駄にコメントアウトは外さないようにしましょう。<br>
<br>
イベントの OnChanged のコメントアウトを外しましょう。<br>
これがテキストが変更されたときに呼ばれるイベントです。<br>
ここに、時折要望として挙がる「テキスト全体の文字数をステータスバーに表示する」処理を書いてみましょう。<br>


<syntaxhighlight lang="csharp">
<syntaxhighlight lang="csharp">
/// <summary>
/// <summary>
/// テキストが変更された時.
/// テキストが変更された時。
/// </summary>
/// </summary>
/// <param name="hWnd">対象のエディタハンドル</param>
/// <param name="hWnd">対象のエディタハンドル</param>
public void OnChanged(IntPtr hWnd) {
public void OnChanged(IntPtr hWnd) {
// ①Mery 操作用のオブジェクト生成
// (1)Mery 操作用のオブジェクト生成
var editor = new Editor(hWnd);
var editor = new Editor(hWnd);


// ②文字数を取得(改行含む)
// (2)文字数を取得(改行含む)
int length = editor.GetLength(true);
int length = editor.GetLength(true);


// ③ステータスバーに結果を表示
// (3)ステータスバーに結果を表示
editor.SetStatus(length.ToString());
editor.SetStatus(length.ToString());
}
}
</syntaxhighlight>
</syntaxhighlight>
<br>
 
どうですか、簡単でしょう!<br>
どうですか、簡単でしょう! 続いて、文字数ではなく Shift-JIS の byte 数にしてみましょう。
続いて、文字数ではなく Shift-JIS の byte 数にしてみましょう。


<syntaxhighlight lang="csharp">
<syntaxhighlight lang="csharp">
/// <summary>
/// <summary>
/// テキストが変更された時.
/// テキストが変更された時。
/// </summary>
/// </summary>
/// <param name="hWnd">対象のエディタハンドル</param>
/// <param name="hWnd">対象のエディタハンドル</param>
public void OnChanged(IntPtr hWnd) {
public void OnChanged(IntPtr hWnd) {
// ①Mery 操作用のオブジェクト生成
// (1)Mery 操作用のオブジェクト生成
var editor = new Editor(hWnd);
var editor = new Editor(hWnd);


// ②文字列を取得
// (2)文字列を取得
string text = editor.GetText();
string text = editor.GetText();


// Shift-JIS のバイト数に変換
// (3) Shift-JIS のバイト数に変換
int length = Encoding.GetEncoding(932).GetByteCount(text);
int length = Encoding.GetEncoding(932).GetByteCount(text);


// ④改行コードが CR+LF だった場合は、その分追加
// (4)改行コードが CR+LF だった場合は、その分追加
if (editor.GetNewLineType() == Editor.NewLineType.CrLf) {
if (editor.GetNewLineType() == Editor.NewLineType.CrLf) {
length += editor.GetLines(true) - 1;
length += editor.GetLines(true) - 1;
}
}


// ⑤ステータスバーに結果を表示
// (5)ステータスバーに結果を表示
editor.SetStatus(length.ToString());
editor.SetStatus(length.ToString());
}
}
</syntaxhighlight>
</syntaxhighlight>
<br>
 
GetLength() は Unicode 文字数を返すので、Shift-JIS のバイト数を取得するためにはまずテキスト全体を取得します(②)。<br>
GetLength() は Unicode 文字数を返すので、Shift-JIS のバイト数を取得するためにはまずテキスト全体を取得します((2))。次に(3)でテキストを Shift-JIS でのバイト数に換算します。正確には Shift-JIS では表現できない文字などの対応が必要になりますが、ここでは省略しています。これで取得できた、としたいのですが、プラグインから取得する文字列の改行コードは全て LF という仕様があります。CR+LF だと改行分だけバイト数がずれてしまいますので、(4)の改行コード判定およびその分の調整をしています。ちょっと面倒ですが、ここまで来ればマクロより楽なはずです。
次に③でテキストを Shift-JIS でのバイト数に換算します。<br>
正確には Shift-JIS では表現できない文字などの対応が必要になりますが、ここでは省略しています。<br>
これで取得できた、としたいのですが、プラグインから取得する文字列の改行コードは全て LF という仕様があります。<br>
CR+LF だと改行分だけバイト数がずれてしまいますので、④の改行コード判定およびその分の調整をしています。<br>
ちょっと面倒ですが、ここまで来ればマクロより楽なはずです。<br>


=== 遅延処理 ===
=== 遅延処理 ===
先ほどのコードで GetText() を使っていますが、覚えているでしょうか……この処理が重いということを。<br>
先ほどのコードで GetText() を使っていますが、覚えているでしょうか……この処理が重いということを。単純に OnChanged の中で使用してしまうと、文字列が変更される度にこの処理が呼ばれます。実際に実行し、a を押しっぱなしにすると、入力される度にステータスバーが更新されます。文字数が少ないうちは良いですが、多くなったときはとてもではないですが実用的な速度で動くことはないでしょう。
単純に OnChanged の中で使用してしまうと、文字列が変更される度にこの処理が呼ばれます。<br>
 
実際に実行し、a を押しっぱなしにすると、入力される度にステータスバーが更新されます。<br>
これを解決するのに、「入力後一定時間変更がなければ更新する」という遅延処理をします。.NET プラグインには独自のタイマー処理が実装されていますので、対応は極めて簡単です。
文字数が少ないうちは良いですが、多くなったときはとてもではないですが実用的な速度で動くことはないでしょう。<br>
<br>
これを解決するのに、「入力後一定時間変更がなければ更新する」という遅延処理をします。<br>
.NET プラグインには独自のタイマー処理が実装されていますので、対応は極めて簡単です。


<syntaxhighlight lang="csharp">
<syntaxhighlight lang="csharp">
/// <summary>
/// <summary>
/// テキストが変更された時.
/// テキストが変更された時。
/// </summary>
/// </summary>
/// <param name="hWnd">対象のエディタハンドル</param>
/// <param name="hWnd">対象のエディタハンドル</param>
public void OnChanged(IntPtr hWnd) {
public void OnChanged(IntPtr hWnd) {
// ①固有のタイマーID
// (1)固有のタイマーID
ushort TimerID = 1;
ushort TimerID = 1;


// ②1000ms 後に処理を開始する
// (2)1000ms 後に処理を開始する
//  1000ms の間に再度呼ばれた場合は後の方が有効になる
//  1000ms の間に再度呼ばれた場合は後の方が有効になる
SetTimer(hWnd, TimerID, 1000, () => {
SetTimer(hWnd, TimerID, 1000, () => {
97行目: 82行目:
}
}
</syntaxhighlight>
</syntaxhighlight>
<br>
 
基本的には、SetTimer() で遅延させているだけです。<br>
基本的には、SetTimer() で遅延させているだけです。これだけで、最後の編集から 1 秒後に反映されるようになり、頻繁な更新を抑制できます。ここでは簡単にするためラムダ式で記述していますが、別のメソッドに仕立てて登録しても良いですし、OnTimer() イベントも用意しているのでそちらで処理しても良いです。
これだけで、最後の編集から 1 秒後に反映されるようになり、頻繁な更新を抑制できます。<br>
 
ここでは簡単にするためラムダ式で記述していますが、別のメソッドに仕立てて登録しても良いですし、OnTimer() イベントも用意しているのでそちらで処理しても良いです。<br>
<br>
このように、いくつかのイベントは呼ばれる度に実行するより遅延させた方が良い場合があります。
このように、いくつかのイベントは呼ばれる度に実行するより遅延させた方が良い場合があります。


== キー操作イベント ==
== キー操作イベント ==
矩形選択は先に Alt を押した状態で選択を開始する必要があります。<br>
矩形選択は先に Alt を押した状態で選択を開始する必要があります。が、プラグインを使うとこの順番を逆にしても使えるようになります。
が、プラグインを使うとこの順番を逆にしても使えるようになります。<br>
 
<br>
今度は「#region PreTranslateMessage のディスパッチ」を開きます。その中の OnSysKeyDown() のコメントアウトを外して、以下のコードを入力します。
今度は「#region PreTranslateMessage のディスパッチ」を開きます。<br>
その中の OnSysKeyDown() のコメントアウトを外して、以下のコードを入力します。


<syntaxhighlight lang="csharp">
<syntaxhighlight lang="csharp">
/// <summary>
/// <summary>
/// システムキーを押したときに呼ばれます.
/// システムキーを押したときに呼ばれます。
/// </summary>
/// </summary>
/// <param name="hWnd">対象のエディタハンドル</param>
/// <param name="hWnd">対象のエディタハンドル</param>
/// <param name="keycode">仮想キーコード.<see cref="VIRTUAL_KEY"/> に定義されています.</param>
/// <param name="keycode">仮想キーコード。<see cref="VIRTUAL_KEY"/> に定義されています。</param>
/// <param name="repeat">リピートカウント</param>
/// <param name="repeat">リピートカウント</param>
/// <param name="alt">ALT キーが押されているか</param>
/// <param name="alt">ALT キーが押されているか</param>
/// <param name="previous">直前のキー状態が指定されます.true の場合,メッセージが送られる前からキーが押されています.</param>
/// <param name="previous">直前のキー状態が指定されます。true の場合、メッセージが送られる前からキーが押されています。</param>
/// <returns>メッセージ処理を継続する場合は false.</returns>
/// <returns>メッセージ処理を継続する場合は false。</returns>
public bool OnSysKeyDown(IntPtr hWnd, int keycode, int repeat, bool alt, bool previous) {
public bool OnSysKeyDown(IntPtr hWnd, int keycode, int repeat, bool alt, bool previous) {
// ①Alt キーが押されたことによるイベントかを判定
// (1)Alt キーが押されたことによるイベントかを判定
if (keycode == (int)VIRTUAL_KEY.VK_MENU) {
if (keycode == (int)VIRTUAL_KEY.VK_MENU) {
// ②選択されていて、まだ矩形選択になっていないか
// (2)選択されていて、まだ矩形選択になっていないか
//  これを確認しないと何度も矩形選択処理をしてしまう
//  これを確認しないと何度も矩形選択処理をしてしまう
var editor = new Editor(hWnd);
var editor = new Editor(hWnd);
if (editor.GetSelType() == Editor.SEL_TYPE.CHAR) {
if (editor.GetSelType() == Editor.SEL_TYPE.CHAR) {
// ③選択範囲を取得
// (3)選択範囲を取得
var start = editor.GetSelStart(true);
var start = editor.GetSelStart(true);
var end = editor.GetSelEnd(true);
var end = editor.GetSelEnd(true);


// ④矩形選択し直す
// (4)矩形選択し直す
editor.Select(start, end, true, true);
editor.Select(start, end, true, true);


// ⑤通常の Alt 処理をキャンセル
// (5)通常の Alt 処理をキャンセル
return true;
return true;
}
}
}
}
// ⑥処理しなかった場合は本体のキー操作処理にゆだねる
// (6)処理しなかった場合は本体のキー操作処理にゆだねる
     return false;
     return false;
}
}
</syntaxhighlight>
</syntaxhighlight>
<br>
 
OnSysKeyDown() は Alt や Ctrl などの特別なキーを処理するためのイベントです。<br>
OnSysKeyDown() は Alt や Ctrl などの特別なキーを処理するためのイベントです。通常の a などのキーは OnKeyDown() になるので、使い分けに注意してください。コードとしては、矩形選択にし直す、という便利なメソッドは用意されていないので、(3)選択範囲の取得→(4)矩形選択し直し、という流れになります。(5)で true を返さないと、本体のキー処理が走るためメニューが表示されてしまいます。無闇にキャンセルしても本体や他のプラグインの邪魔をしてしまうのでよくありませんが、今回ぐらいであれば true で以降の処理をキャンセルした方が良いでしょう。
通常の a などのキーは OnKeyDown() になるので、使い分けに注意してください。<br>
コードとしては、矩形選択にし直す、という便利なメソッドは用意されていないので、③選択範囲の取得→④矩形選択し直し、という流れになります。<br>
⑤で true を返さないと、本体のキー処理が走るためメニューが表示されてしまいます。<br>
無闇にキャンセルしても本体や他のプラグインの邪魔をしてしまうのでよくありませんが、今回ぐらいであれば true で以降の処理をキャンセルした方が良いでしょう。<br>
<br>

2023年5月20日 (土) 02:29時点における最新版

概要[編集]

イベント(テキスト変更、キー操作)対応の説明。

テキスト変更イベント[編集]

プラグインの基本は、発生するイベントに応じて処理していくことです。まずは使うことの多いであろう、テキスト変更イベントを処理してみます。

ソースの中で「#region OnEvents のディスパッチ」が閉じられていると思いますので、これを開きます。基本的に対応しているイベントは全て書かれていますので、コメントを外すことで各イベント発生時に呼ばれるようになります。なおコメントアウトしておけば無駄に呼ばれることがないため、遅くしないためにも無駄にコメントアウトは外さないようにしましょう。

イベントの OnChanged のコメントアウトを外しましょう。これがテキストが変更されたときに呼ばれるイベントです。ここに、時折要望として挙がる「テキスト全体の文字数をステータスバーに表示する」処理を書いてみましょう。

/// <summary>
/// テキストが変更された時。
/// </summary>
/// <param name="hWnd">対象のエディタハンドル</param>
public void OnChanged(IntPtr hWnd) {
	// (1)Mery 操作用のオブジェクト生成
	var editor = new Editor(hWnd);

	// (2)文字数を取得(改行含む)
	int length = editor.GetLength(true);

	// (3)ステータスバーに結果を表示
	editor.SetStatus(length.ToString());
}

どうですか、簡単でしょう! 続いて、文字数ではなく Shift-JIS の byte 数にしてみましょう。

/// <summary>
/// テキストが変更された時。
/// </summary>
/// <param name="hWnd">対象のエディタハンドル</param>
public void OnChanged(IntPtr hWnd) {
	// (1)Mery 操作用のオブジェクト生成
	var editor = new Editor(hWnd);

	// (2)文字列を取得
	string text = editor.GetText();

	// (3) Shift-JIS のバイト数に変換
	int length = Encoding.GetEncoding(932).GetByteCount(text);

	// (4)改行コードが CR+LF だった場合は、その分追加
	if (editor.GetNewLineType() == Editor.NewLineType.CrLf) {
		length += editor.GetLines(true) - 1;
	}

	// (5)ステータスバーに結果を表示
	editor.SetStatus(length.ToString());
}

GetLength() は Unicode 文字数を返すので、Shift-JIS のバイト数を取得するためにはまずテキスト全体を取得します((2))。次に(3)でテキストを Shift-JIS でのバイト数に換算します。正確には Shift-JIS では表現できない文字などの対応が必要になりますが、ここでは省略しています。これで取得できた、としたいのですが、プラグインから取得する文字列の改行コードは全て LF という仕様があります。CR+LF だと改行分だけバイト数がずれてしまいますので、(4)の改行コード判定およびその分の調整をしています。ちょっと面倒ですが、ここまで来ればマクロより楽なはずです。

遅延処理[編集]

先ほどのコードで GetText() を使っていますが、覚えているでしょうか……この処理が重いということを。単純に OnChanged の中で使用してしまうと、文字列が変更される度にこの処理が呼ばれます。実際に実行し、a を押しっぱなしにすると、入力される度にステータスバーが更新されます。文字数が少ないうちは良いですが、多くなったときはとてもではないですが実用的な速度で動くことはないでしょう。

これを解決するのに、「入力後一定時間変更がなければ更新する」という遅延処理をします。.NET プラグインには独自のタイマー処理が実装されていますので、対応は極めて簡単です。

/// <summary>
/// テキストが変更された時。
/// </summary>
/// <param name="hWnd">対象のエディタハンドル</param>
public void OnChanged(IntPtr hWnd) {
	// (1)固有のタイマーID
	ushort TimerID = 1;

	// (2)1000ms 後に処理を開始する
	//  1000ms の間に再度呼ばれた場合は後の方が有効になる
	SetTimer(hWnd, TimerID, 1000, () => {
		var editor = new Editor(hWnd);
		string text = editor.GetText();
		int length = Encoding.GetEncoding(932).GetByteCount(text);
		if (editor.GetNewLineType() == Editor.NewLineType.CrLf) {
			length += editor.GetLines(true) - 1;
		}
		editor.SetStatus(length.ToString());
	});
}

基本的には、SetTimer() で遅延させているだけです。これだけで、最後の編集から 1 秒後に反映されるようになり、頻繁な更新を抑制できます。ここでは簡単にするためラムダ式で記述していますが、別のメソッドに仕立てて登録しても良いですし、OnTimer() イベントも用意しているのでそちらで処理しても良いです。

このように、いくつかのイベントは呼ばれる度に実行するより遅延させた方が良い場合があります。

キー操作イベント[編集]

矩形選択は先に Alt を押した状態で選択を開始する必要があります。が、プラグインを使うとこの順番を逆にしても使えるようになります。

今度は「#region PreTranslateMessage のディスパッチ」を開きます。その中の OnSysKeyDown() のコメントアウトを外して、以下のコードを入力します。

/// <summary>
/// システムキーを押したときに呼ばれます。
/// </summary>
/// <param name="hWnd">対象のエディタハンドル</param>
/// <param name="keycode">仮想キーコード。<see cref="VIRTUAL_KEY"/> に定義されています。</param>
/// <param name="repeat">リピートカウント</param>
/// <param name="alt">ALT キーが押されているか</param>
/// <param name="previous">直前のキー状態が指定されます。true の場合、メッセージが送られる前からキーが押されています。</param>
/// <returns>メッセージ処理を継続する場合は false。</returns>
public bool OnSysKeyDown(IntPtr hWnd, int keycode, int repeat, bool alt, bool previous) {
	// (1)Alt キーが押されたことによるイベントかを判定
	if (keycode == (int)VIRTUAL_KEY.VK_MENU) {
		// (2)選択されていて、まだ矩形選択になっていないか
		//  これを確認しないと何度も矩形選択処理をしてしまう
		var editor = new Editor(hWnd);
		if (editor.GetSelType() == Editor.SEL_TYPE.CHAR) {
			// (3)選択範囲を取得
			var start = editor.GetSelStart(true);
			var end = editor.GetSelEnd(true);

			// (4)矩形選択し直す
			editor.Select(start, end, true, true);

			// (5)通常の Alt 処理をキャンセル
			return true;
		}
	}
	// (6)処理しなかった場合は本体のキー操作処理にゆだねる
    return false;
}

OnSysKeyDown() は Alt や Ctrl などの特別なキーを処理するためのイベントです。通常の a などのキーは OnKeyDown() になるので、使い分けに注意してください。コードとしては、矩形選択にし直す、という便利なメソッドは用意されていないので、(3)選択範囲の取得→(4)矩形選択し直し、という流れになります。(5)で true を返さないと、本体のキー処理が走るためメニューが表示されてしまいます。無闇にキャンセルしても本体や他のプラグインの邪魔をしてしまうのでよくありませんが、今回ぐらいであれば true で以降の処理をキャンセルした方が良いでしょう。

スポンサーリンク