Home > Archives > 2009-07

2009-07

猫ウィンドウクラスを作る(5)

●利用側で機能拡張できるようにする

CCatWndを使う側では,追加したメニュー項目選択時の処理や,猫ウィンドウが特に処理を行っていないメッセージに対して処理を追加したいはずなので,それができるようにしておきます.

 


 

●追加処理オブジェクトを指定させる

機能拡張のために「CCatWndを継承させる」というのでもいいのですが,今回はなんとなく
「CCatWndにコールバック関数を設定しておいて,WndProc()の中でこの関数をコールしてやる」という類の方針を取りました.
コールバック関数の型は

bool AdditionalWndProc( class CCatWnd *pCatWnd, UINT message, WPARAM wParam, LPARAM lParam );

みたいな.
実際には以下のように,関数ではなく,そういう関数をメンバにもつクラスのオブジェクトを指定する形になっていますが,意味は一緒です.

//猫ウィンドウとともに使う外部処理IF
//アプリはこれを継承したクラスを作り,猫ウィンドウにセットして使う.
class CCatWndManager
{
public:
 virtual ~CCatWndManager(){}

public:
 //追加イベント処理関数.
 //猫ウィンドウは,内部で必要なメッセージに対応する処理を行ったあとで,
 //ウィンドウプロシージャ内(の最後らへん)でこの関数をコールする.
 //(ただし,こないメッセージもある.
 // WM_RBUTTONUPはポップアップに使われるので来ない.
 // あと,猫ウィンドウのアニメーション用タイマのイベントとかも)
 //
 //メッセージを処理したらtrueを,処理しない場合はfalseを返すこと.
 //(::DefWindowProc()をコールする類の処理は猫ウィンドウが行う)
 virtual bool AdditionalWndProc( class CCatWnd *pCatWnd, UINT message, WPARAM wParam, LPARAM lParam ){ return false; }
};

CCatWnd側には設定用の関数を用意します.

void SetCatManager( CCatWndManager *pCatManager );

CCatWnd側は,設定がなされていれば,WndProc()内から設定されたオブジェクトのAdditionalWndProc()を呼びます.
CCatWnd側で既定の処理があるメッセージの際にも呼ぶか?ということについては考え方次第かもしれませんが,とりあえず今は↑の注釈にあるように,
「呼ばれないメッセージもある」という,「じゃあ何のメッセージのときは呼ばれるのか」が把握できないなんとも嫌な感じに仕上げてみました.
しかしこれだと,アニメーション選択に最もふさわしいであろうタイミングである「アニメーションフレームを切り替える時」に呼ばれなくなってしまうので,CCatWndManagerに

//アニメーションのフレーム切換え前にコールされる.
 //引数は今の(切換え前の)アニメーションのAnimIDとFrameIndex.
 //
 //trueを返すとデフォルト動作(次のフレームに進める)処理が行われる.
 //デフォルト動作を行わせないときはfalseを返すこと.
 virtual bool OnPreChangeAnimFrame( class CCatWnd *pCatWnd, unsigned int AnimID, unsigned int FrameIndex );

とかいう特別な関数が追加されたりとかグダグダな感じになっています……なんだかなぁ.

 


 

●CCatWndに必要なメソッドを追加する

これでCCatWndManager(を継承したクラス)からCCatWndを操作できるので,アプリケーション独自の処理を追加できます.
ただし,今のままでは「CCatWndに対してできること」が少なすぎる(メニュー項目の追加とアニメーションの選択くらいしかない)ので満足に操作できません.

CCatWndに今後publicなメソッドを増やしていく必要がありそうです.

CCatWndにウィンドウハンドルを返す関数を持たせれば,かなり自由に操作できるようになりますが,あまりにも自由すぎて「SetWindowLongでウィンドウプロシージャ置き換えたよ!」とか言われても困るので,現時点ではウィンドウハンドルは外部に教えない方針です.

とりあえず現在の最終目標は「黒猫徘徊ソフト」なので,徘徊に必要そうな機能(ウィンドウ位置を返す関数やウィンドウを移動する関数等)から順次追加されている最中です.

づづく...

  • Comments (Close): 0
  • Trackbacks (Close): 0

猫ウィンドウクラスを作る(4)

●猫を右クリックでポップアップメニューが出るようにする

猫ウィンドウがデフォルトで唯一提供するGUIであるポップアップメニューを実装します.
そして,このポップアップメニューを猫ウィンドウを閉じる手段として利用することにします.

 

 

 


  

 

●メニューを作る

ポップアップメニューは::CreatePopupMenu()で作り,::DestroyMenu()で破棄します.
WM_CREATE時にメニューを作成し,WM_DESTROY時に破棄することにします.
CCatWndに,作ったメニューのハンドルを保持するためのm_hPopupMenuを追加し,コンストラクタでNULLに初期化しておきます.

//ポップアップメニュー作成
//※CAT_WND_MENU_ID_EXITは適当な値の定数
bool CCatWnd::CreateDefaultPopupMenu()
{
 if( m_hPopupMenu != NULL )return true;
 m_hPopupMenu = ::CreatePopupMenu();
 if( m_hPopupMenu == NULL )return false;

 return InsertPopupMenuItem( 0, CAT_WND_MENU_ID_EXIT, std::string("Exit") );
}

//ポップアップメニュー破棄
bool CCatWnd::DestroyPopupMenu()
{
 if( m_hPopupMenu )
 {
  if( ::DestroyMenu( m_hPopupMenu ) )
  {
   m_hPopupMenu = NULL;
   return true;
  }
  else
  { return false; }
 }

 return true;
}

//メニューへの項目追加
bool CCatWnd::InsertPopupMenuItem( UINT PosIndex, UINT ItemID, const std::string &rItemString )
{
 if( !m_hPopupMenu )return false;
 if( rItemString.empty() )return false;

 MENUITEMINFO miinfo = { 0 };
 miinfo.cbSize = sizeof( MENUITEMINFO );
 miinfo.fMask = MIIM_ID | MIIM_STRING;
 miinfo.wID = ItemID;
 miinfo.cch = (UINT)( rItemString.length() );
 std::vector< char > Data( rItemString.begin(), rItemString.end() ); //※constでないものを要求されるのでコピーを生成して対応
 Data.push_back( '' );
 miinfo.dwTypeData = &( Data.at(0) );

 bool ret = ( ::InsertMenuItem( m_hPopupMenu, PosIndex, TRUE, &miinfo ) != 0 );
 if( ret )
 { ::DrawMenuBar( m_hWnd ); }
 return ret;
}

ポップアップメニューの項目はデフォルトでは終了のための”Exit”のみですが,項目を追加するInsertPopupMenuItem()をpublicにすることで,利用側からの拡張を可能にしています.

 

 

 


  

 

●ポップアップメニューの表示と処理

WndProc()内で,猫を右クリックしたら作っておいたメニューを表示するようにします.
(万が一メニュー作成等に失敗していた場合,ウィンドウを閉じる手段が無くなってしまうので,そのための対策つき)

case WM_RBUTTONUP:
  {
   //ポップアップメニュー表示
   bool bTrackPopup = false;
   if( m_hPopupMenu )
   {
    //座標取得
    int x=0 ,y=0;
    {
     POINT pos;
     pos.x = LOWORD( lParam );
     pos.y = HIWORD( lParam );
     ::ClientToScreen( m_hWnd, &pos );
     x = pos.x;
     y = pos.y;
    }

    //ポップアップメニュー
    bTrackPopup = ( ::TrackPopupMenu( m_hPopupMenu, TPM_LEFTALIGN|TPM_BOTTOMALIGN, x,y, 0, m_hWnd, NULL ) != 0 );
   }

   //メニュー関連に失敗しているとき用の念のための対策
   if( !bTrackPopup )
   { DestroyWnd(); }
  }
  break;

ポップアップメニューを表示する::TrackPopupMenu()関数は,「ポップアップメニュー表示中は処理が返ってこないのに,他のイベント処理はその間も動作している」という謎の動作をしていて不安をあおります…
メニュー項目が選択されるとWM_COMMANDメッセージがくるので,そこに処理を記述します.
メニューのイベント時にはHIWORD(wParam)が0になるとのことなので,それでメニューのイベントであることを判断しています.

case WM_COMMAND:
  {
   if( HIWORD(wParam)==0 ) //メニューからのメッセージか?
   {
    switch( LOWORD(wParam) )
    {
    case CAT_WND_MENU_ID_EXIT:
     DestroyWnd();
     break;

    default:
     break;
    }
   }
  }
  break;

これで 右クリック→メニュー出る→”Exit”選択でウィンドウ閉じる が達成できました.
(このままではまだ,追加されたメニュー項目については処理できませんが.)

 

 

 


  

 

●アプリ終了へ向かわすには?

ウィンドウは閉じられるようになりましたが,このときにアプリケーションを終了に向かわせるかどうかは,猫ウィンドウの役どころによって変わってきます.
猫ウィンドウがメインウィンドウとして使われているなら,WM_DESTROY時にPostQuitMessage()APIを呼ぶようにします.

//メインウィンドウ指定されている場合,アプリ終了へ向かわす
   if( this->IsMainWnd() )//※IsMainWnd()はメインウィンドウ指定されているかどうかを返すメンバ関数
   { PostQuitMessage(0); }

メインウィンドウとして使うか否かは,ウィンドウ生成時に引数で設定するようにしました.

猫ウィンドウクラスを作る(5)

  • Comments (Close): 0
  • Trackbacks (Close): 0

Home > Archives > 2009-07

Search
Feeds
Meta

Return to page top