Mesoscopic Programming

タコさんプログラミング専門

ヌメロン製作講座第10回:ボタン処理の作成

ボタンを押したらどうなりますか?こう成増。

ボタンが押された状態

f:id:hidakas1961:20120920235250j:plain

修正ソースファイル
  1. main.h
  2. main.cpp

まだマウスには対応していないので、ボタンを押すにはカーソルキーで選択してリターンキーを押すかスペースキーを押してください。


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;
    }
}

押された(実際には離された時ですが)ボタンに対応する関数をコールするだけですね。
これらはメニューコマンドと完全にリンクしているので、実装箇所はアプリケーションクラスになります。

以上でボタン処理は終了です。
次回はマウス処理に対応しましょうか、そうしましょう。