2008年06月09日

効率の良いメニュー処理を考える。

現段階でのメニュー処理はポップアップ(サブメニュー)ごとのインデクスを持った連番のIDを与え
それに対応した配列を持つことでメインメニューの数だけの分岐で済ましているが
ラジオボタンの処理などを加える場合サブニューの取得などが必要になる。
凝るべきところではないがこの程度はやってもよかろうということで
もう少しメニュー処理を掘り下げ効率的にしてみた。

IDがユニークな場合、それを探すというのも手ではある。
木構造の処理はどうせ実装するのだから取っ掛かりに悪くは無いし効率も重視しない。
なぜならWM_COMMANDによる処理はあまり急がない場合が多いからだ。
しかしまぁ納得のいく無駄の無いコードを模索してみた。

現状相変わらずDispatchMessageは使わずPeekMessage後直接処理をして「その他」でWindowProcを呼んでいるのだが

メッセージループ(Windowprocではなく)のWM_COMMANDの処理を

Oncommand(App_Message.wParam);

からwindowprocの

if lparam=0 then Oncommand(wParam);

に変えてみた。

lparamのチェックはコントロールでなくてメニューであるということの確認で
これをやるということは複数Windowもありうるのでこの形にした。
コントロールは一切使わないし1windowなのでこの変更には意味がないが汎用性モデルとして
この措置は今回だけ

次にWindowメッセージを調べた。これはそこいらでもある内容だが

WM_ENTERMENULOOP

WM_INITMENU

WM_INITMENUPOPUP

WM_MENUSELECT

WM_ENDMENULOOP

WM_COMMAND


の順でメッセージが発行されこのうちWM_COMMAND以外はメッセージループにはポストされず
直接WindowProcに投げられる事がわかった。

またポップアップなどはブロック命令ではなくて
メッセージループとは別に直接Windowprocへの呼び出しを持つ
「OS内のループ処理」であることが
それぞれの処理が出来ることから読める。

アニメーションが止まってしまうのを防ぐのはちょっと高度な処理がいりそうだが
フリーズするわけではないのでWM_ENTERMENULOOPからWM_ENDMENULOOPまでの処理はいらないだろう。
全てのポップアップをWindowメニューに関連付けているわけだが
trackpopup命令でもWM_INITMENUは呼ばれ、そのときのlparamがポップアップのメニューハンドルであることと
「現在のメニューハンドル」はWM_INITMENUとWM_INITMENUPOPUPのどちらかが最後に取得したlparamであるということが分かる。
メニューアイテムの操作命令ではハンドルをgetsubmenu等で得るのが定石だがこれで既に得られているということになる。

次にWM_MENUSELECTだがこれはメニュー移動時にヘルプを出す用途以外では
IDか位置か判別しなくてはいけないので一律な処理では扱えないのだが
例えばメニューをダラダラ移動した後にメニューバー内の一発コマンドを選んだ場合
WM_INITMENUPOPUPでは正しいメニューIDが取得できない。
しかしWM_MENUSELECTに入る直前にはWM_INITMENUPOPUPで「位置」の情報が出ているので
そのときの値を保持することにした。

結果としてメニューモーダルにはいったことを
WM_ENTERMENULOOP

最終的にどのメニューが実施されたかは
WM_MENUSELECT
のlparamが0でないときを保持することで得るというのが良いという結論に至った。


menu_parambuf,menu_param:LongWord;

以下のコードをWindowProcにいれた。

$211://WM_ENTERMENULOOP
begin
menu_param:=0;
onmenuLoop;
end;

$117://WM_INITMENUPOPUP
menu_parambuf:=menu_param;

$11F://WM_MENUSELECT
if lparam<> 0 then begin
menu_param:=wparam;
sel_menu:=lparam;
end;

menu_paramで最終コマンドがmenu_parambufでメニューインデクスが取得できた。
WM_COMMANDはmenu_parambufで振り分ければよいから
今回は修正していないが各メニューに素直に0から数字をふっても良いだろう。
そうすることでメニューIDのWORD値をそのメニュー内でフルに使えるから
グループ分けなどしやすい。

今回の場合、windowサイズなどの変更の実験からそのまま作っており
一つのポップアップが1系統の内容になっているので
ラジオボタンをつける場合WM_COMMANDで

CheckMenuRadioItem(sel_menu,0,$6FFF,word(menu_param),0 );

で選んだメニューの横にラジオボタンがつく。

無論今回のような方法を取らずmenuハンドルから位置を割り出すルーチンを使ってもよいと思う。
なぜならここは速度がいらないところだからだ。
それでも今回の結果物はそこそこ妥協できるものではないかと思う。

ステータス系の命令はCheckMenuRadioItemの他に

CheckMenuItem(sel_menu,word(menu_param),8 );//MF_CHECKED
EnableMenuItem(sel_menu, word(menu_param), 1);//MF_GRAYED
ModifyMenu(sel_menu, word(menu_param), 0, word(menu_param),lstr);

などで選択したメニュー項目を操作できる。
トグル系のコマンドで特に重宝しそうだ。

日本語はマルチバイトで扱いたくないのでModifyMenuで日本語対応の場合は
ModifyMenuWにするかBITMAPにするかということになる。

今回の実験でWM_COMMANDが随分とコンパクトに実装できそうな感じになった。

当分はonmenuLoopは空でいいと思っている。
なぜならFPS動作ではタイマーベースで実装するつもりだからだ。
ここで処理が必要なのは単純にカウンターを進めて動作するようなケースだろう。

メニューとコマンドラインについては基本的にこの程度にして
メッセージボックスのポップアップ化=最前面対策をやろうと思う。

ラベル:メニュー
posted by Xo_ox at 20:38| Comment(1) | Win出直し | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
FPSゲームに使用する特別なマウスがあります。
このマウスの左ボタンを押していると、
1秒に6~7回を自動でクリックします。
敵に正確に照準して射撃できます。
使用者が自動クリック速度を調節できます。
詳しい情報は...
http://www.automouse.jp
Posted by shooting star at 2008年06月10日 15:05
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
×

この広告は1年以上新しい記事の投稿がないブログに表示されております。