ヌメロン製作講座第10回:ボタン処理の作成
ボタンを押したらどうなりますか?こう成増。
ボタンが押された状態
class Application
アプリケーションクラスには1つのメソッドを追加しました。
class Application : public Window { ~中略~ VOID OnKeyUp(); ~中略~ };
VOID Application :: OnKeyUp()
それはキーアップメッセージ処理です。
VOID Application :: OnKeyUp() { UINT vkey = LOWORD( wparam ); setGrid.OnKeyUp( vkey ); lresult = 0; }
キーが離されたときにその仮想キーコードをグリッドクラスに通知するだけです。
class Grid
そしてグリッドクラスには1つの変数と2つのメソッドを追加しました。
class Grid { ~中略~ UINT pushKey; ~中略~ virtual VOID Push( BOOL sw ); virtual VOID OnKeyUp( UINT vkey ); ~中略~ };
pushKey は現在押されている仮想キーコードです。
初期化時にゼロクリアし OnKeyDown() でセットします。
VOID Grid :: OnKeyDown()
修正個所だけ記載します。
VOID Grid :: OnKeyDown( UINT vkey ) { ~中略~ if ( pushKey != 0 ) { return; } switch ( vkey ) { case VK_RETURN : if ( cell0.cid >= CELL_EDIT ) { OnEditBegin(); return; } case VK_SPACE : if ( cell0.cid == CELL_BUTTON ) { pushKey = vkey; Push( TRUE ); return; } break; ~中略~ } }
同時に複数のボタンを押すことはできないので、現在ボタンが押された状態ならば新たなキーダウン通知は無視する処理を冒頭に追加しました。
そしてもしボタンが選択された状態でリターンキーもしくはスペースキーが押されたなら pushKey に押されたキーをセットし、押された状態を表示するために Push() 関数をコールしています。
VOID Grid :: OnKeyUp()
アプリケーションから通知されたキーアップ通知に対応します。
VOID Grid :: OnKeyUp( UINT vkey ) { Cell & cell = GetCells()[ select ]; if ( vkey == pushKey ) { Push( FALSE ); pushKey = 0; cell.OnButton(); } }
離されたキーが前回押されたキーならばそれはボタンです。ボタンの処理は押されたときではなく離されたときに行います。
ボタンが離された状態を再描画したのち、セルオブジェクトに対してボタン処理を行うよう OnButton() 関数をコールします。
VOID Grid :: Push()
ボタンが押された状態を表示するための関数です。
VOID Grid :: Push( BOOL sw ) { Cell * cells = GetCells(); Cell & cell = cells[ select ]; RECT rc; if ( sw ) { cell.state |= STATE_PUSH; } else { cell.state &= ~ STATE_PUSH; } cell.GetRect( rc, offset ); InvalidateRect( hwnd, & rc, FALSE ); }
セルの状態フラグにはボタンが押された状態を表す STATE_PUSH フラグを追加しました。このフラグが立っているとき、セルはボタンが押された状態を描画します。そしてこのボタンを再描画させるために InvalidateRect() しています。
グリッドクラスの追加内容は以上です。
struct Cell
続いてセル構造体の修正および追加内容について述べます。
セル構造体に追加されたメソッドは以下の1つです。
struct Cell { ~中略~ VOID OnButton(); };
そのメソッドとは OnButton()、つまりボタン機能の実行処理です。
しかしその前にボタン描画処理の修正点を見てみましょう。
BOOL Cell :: DrawFrame()
まずは枠の描画から。
BOOL Cell :: DrawFrame( HDC hdc, RECT rc ) { ~中略~ if ( cid == CELL_BUTTON ) { ~中略~ if ( ( state & STATE_PUSH ) != 0 ) { FrameRect( hdc, & rc, app.hbrDarkGray ); rc.right--; rc.bottom--; FrameRect( hdc, & rc, app.hbrBlack ); rc.left++; rc.top++; FrameRect( hdc, & rc, app.hbrLightGray ); rc.right--; rc.bottom--; FrameRect( hdc, & rc, app.hbrDarkGray ); rc.left++; rc.top++; FillRect( hdc, & rc, app.hbrLightGray ); } else { FrameRect( hdc, & rc, app.hbrBlack ); rc.right--; rc.bottom--; FrameRect( hdc, & rc, app.hbrWhite ); rc.left++; rc.top++; FrameRect( hdc, & rc, app.hbrDarkGray ); rc.right--; rc.bottom--; FillRect( hdc, & rc, app.hbrLightGray ); } ~中略~ }
押されているときは通常よりもへっ込んで見えるように描きます。これは錯視であり、実際に画面がへっ込んでいるわけではありません。これぞメンタルマジックですね。違うか。
BOOL Cell :: DrawContent()
続いてボタンの中身を描くやつの修正です。
BOOL Cell :: DrawContent( HDC hdc, RECT rc ) { ~中略~ else if ( cid == CELL_BUTTON ) { format |= DT_CENTER; if ( ( state & STATE_PUSH ) != 0 ) { rc.left++; rc.top++; rc.right++; rc.bottom++; } } ~中略~ }
枠がへっ込んだので、同時に中身もへっ込まないと不自然でしょう。そういうことです。
VOID Cell :: OnButton()
最後にボタン機能実行処理を見てみましょう。
VOID Cell :: OnButton() { switch ( GetParamID() ) { case PARAM_START_BUTTON : app.OnSwitchMode(); break; case PARAM_END_BUTTON : app.OnAppExit(); break; case PARAM_NEW_BUTTON : app.OnFileNew(); break; case PARAM_LOAD_BUTTON : app.OnFileOpen(); break; case PARAM_SAVE_BUTTON : app.OnFileSave(); break; case PARAM_CLEAR_BUTTON : app.OnClearRecord(); break; case PARAM_ANAL_BUTTON : app.OnSwitchAnal(); break; case PARAM_RETURN_BUTTON : app.OnSwitchMode(); break; } }
押された(実際には離された時ですが)ボタンに対応する関数をコールするだけですね。
これらはメニューコマンドと完全にリンクしているので、実装箇所はアプリケーションクラスになります。
以上でボタン処理は終了です。
次回はマウス処理に対応しましょうか、そうしましょう。