ポップアップ


ショートカットメニュー

ほとんどの Windows アプリケーションは
クライアント領域で右クリックすると、何らかのポップアップメニューが表示されます
これをショートカットメニューと呼び、メニューと同様に使うことができます

ショートカットメニュを表示させるには、まずメニューのハンドルが必要です
メニューのハンドルを取得するには LoadMenu() 関数を使えば良いですね

ただし、ショートカットメニューが表示するのは単一のポップアップです
そこで、メニューからトップレベルのポップアップのハンドルを取得します
ポップアップメニューのハンドルを取得するには GetSubMenu() を用います

HMENU GetSubMenu(HMENU hMenu , int nPos);

hMenu には、親メニューのハンドルを指定します
nPos には、ドロップダウンメニュー(サブメニュ)の開く項目を 0 から相対位置で指定します
戻り値は、指定したメニューのハンドルになりますが、無い場合は NULL が返ります

この関数で、表示するポップアップメニューのハンドルが取得できれば
次は、ポップアップを表示する TrackPopupMenu() 関数を使います
BOOL TrackPopupMenu(
	HMENU hMenu ,
	UINT uFlags ,
	int x ,  int y ,
	int nReserved,
	HWND hWnd, 
	CONST RECT *prcRect
);
hMenu は、表示するポップアップのハンドルを指定します
uFlags は、メニューの表示方法などを以下の定数で指定します

定数解説
水平方向
TPM_CENTERALIGN ショートカットメニューの中心を、x パラメータが指定する座標に合わせます
TPM_LEFTALIGN ショートカットメニューの左端を、x パラメータが指定する座標に合わせます
TPM_RIGHTALIGN ショートカットメニューの右端を、x パラメータが指定する座標に合わせます
垂直方向
TPM_BOTTOMALIGN ショートカットメニューの下端を、y パラメータが指定する座標に合わせます
TPM_TOPALIGN ショートカットメニューの上端を、y パラメータが指定する座標に合わせます
TPM_VCENTERALIGN ショートカットメニューの中心を、y パラメータが指定する座標に合わせます
親ウィンドウが無い場合
TPM_NONOTIFY ユーザーがメニュー項目をクリックしたとき、通知メッセージを送りません
TPM_RETURNCMD 関数の戻り値として、ユーザーが選択したメニュー項目の ID を返します
マウスボタン処理
TPM_LEFTBUTTON マウスの左ボタンでポップアップ メニューからの選択が行えるようにします
TPM_RIGHTBUTTON マウスの右ボタンでポップアップ メニューからの選択が行えるようにします

x と y には、メニューの表示位置の X 座標と Y 座標をスクリーン座標で指定します
nReserved は予約されている引数で、必ず 0 を指定しなければなりません
hWnd は、ショートカットメニューを持つウィンドウのハンドルを指定します
このウィンドウのウィンドウプロシージャにメッセージが送られてくるようになっています

prcRect はメニューが有効である範囲を表す RECT 構造体へのポインタです
この長方形の外側をクリックすると、メニューはウィンドウから消えてしまいます
NULL を指定するとメニューの外側のクリックによってショートカットメニューが解放されます
関数が成功すると 0 以外、失敗すると 0 が返ります

メニューの表示位置は、スクリーン座標で指定しなければなりません
カーソルなどの座標はクライアント座標なので、これをスクリーン座標にする必要があります
この場合は、これを計算してくれる便利な関数 ClientToScreen() を使います

BOOL ClientToScreen(HWND hWnd , LPPOINT lpPoint);

hWnd は、ウィンドウのハンドルを指定します
ここで指定したウィンドウのクライアント座標であると判断されます
lpPoint はクライアント座標が格納されている POINT 構造体へのポインタです
この構造体のないようが、スクリーン座標に変換されます
関数が成功すると 0 以外、失敗すると 0 が返ってきます

ちなみに、これと逆の変換をしてくれる ScreenToClient() という関数もあります

BOOL ScreenToClient(HWND hWnd , LPPOINT lpPoint);

引数や戻り値の意味は ClientToScreen() とまったく同じです
hWnd で指定したウィンドウのクライアント座標に変換します
//リソーススクリプト
KITTY MENU {
	POPUP "PopupMenu1" {
		MENUITEM "Card Captor Sakura" , 40001
		POPUP "LOVE HINA" {
			MENUITEM "成瀬川なる" , 40002
			MENUITEM "前原しのぶ" , 40003
			MENUITEM "浦島カナコ(偽妹)" , 40004
		}
	}
	POPUP "PopupMenu2" {
		MENUITEM "Kitty on your lap" , 40005
		MENUITEM SEPARATOR
		MENUITEM "Di Gi Charat" , 40006
		MENUITEM "Magical nyan nyan TARUTO"
	}
}
#include <windows.h>

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	POINT po;
	static HMENU tmp , hmenuR , hmenuL;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		tmp = LoadMenu(((LPCREATESTRUCT)(lp))->hInstance , TEXT("KITTY"));
		hmenuR = GetSubMenu(tmp , 0);
		hmenuL = GetSubMenu(tmp , 1);
		return 0;
	case WM_RBUTTONUP:
		po.x = LOWORD(lp);
		po.y = HIWORD(lp);
		ClientToScreen(hwnd , &po);
		TrackPopupMenu(hmenuR , TPM_LEFTALIGN | TPM_BOTTOMALIGN ,
			po.x , po.y , 0 , hwnd , NULL
		);
		return 0;
	case WM_LBUTTONUP:
		po.x = LOWORD(lp);
		po.y = HIWORD(lp);
		ClientToScreen(hwnd , &po);
		TrackPopupMenu(hmenuL , TPM_LEFTALIGN | TPM_BOTTOMALIGN ,
			po.x , po.y , 0 , hwnd , NULL
		);
		return 0;
	}
	return DefWindowProc(hwnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow ) {
	HWND hwnd;
	MSG msg;
	WNDCLASS winc;

	winc.style		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return -1;

	hwnd = CreateWindow(
			TEXT("KITTY") , TEXT("Kitty on your lap") ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL ,
			hInstance , NULL
	);

	if (hwnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}


ショートカットメニューも、ポップアップの仕様はメニューと同じです
このプログラムは、右クリックすると "PopupMenu1" を
左クリックすると "PopupMenu2" を表示するプログラムの例です


GetSubMenu()

HMENU GetSubMenu(HMENU hMenu , int nPos);

ドロップダウンメニュー、またはサブメニューのハンドルを取得します

hMenu - 親メニューのハンドル
nPos - 開くメニューの項目の位置

戻り値 - メニューのハンドル。無い場合は NULL

TrackPopupMenu()

BOOL TrackPopupMenu(
	HMENU hMenu ,
	UINT uFlags ,
	int x ,  int y ,
	int nReserved,
	HWND hWnd, 
	CONST RECT *prcRect
);
指定された位置にショートカットメニューを表示
画面上のどこにでも表示することができます

hMenu - 表示するポップアップのハンドルを指定します
uFlags - メニューの表示方法などを定数で指定します
x - 表示するスクリーン座標の X 座標を指定します
y - 表示するスクリーン座標の Y 座標を指定します
nReserved - 0 を指定しなければなりません
hWnd - メニューを所持するウィンドウのハンドルを指定します
prcRect - 使用できるスクリーンの範囲を格納した RECT 構造体へのポインタ

戻り値 - 成功すれば 0 以外、失敗すれば 0

uFlags には以下の定数を組み合わせて指定します

定数解説
水平方向
TPM_CENTERALIGN ショートカットメニューの中心を、x パラメータが指定する座標に合わせます
TPM_LEFTALIGN ショートカットメニューの左端を、x パラメータが指定する座標に合わせます
TPM_RIGHTALIGN ショートカットメニューの右端を、x パラメータが指定する座標に合わせます
垂直方向
TPM_BOTTOMALIGN ショートカットメニューの下端を、y パラメータが指定する座標に合わせます
TPM_TOPALIGN ショートカットメニューの上端を、y パラメータが指定する座標に合わせます
TPM_VCENTERALIGN ショートカットメニューの中心を、y パラメータが指定する座標に合わせます
親ウィンドウが無い場合
TPM_NONOTIFY ユーザーがメニュー項目をクリックしたとき、通知メッセージを送りません
TPM_RETURNCMD 関数の戻り値として、ユーザーが選択したメニュー項目の ID を返します
マウスボタン処理
TPM_LEFTBUTTON マウスの左ボタンでポップアップ メニューからの選択が行えるようにします
TPM_RIGHTBUTTON マウスの右ボタンでポップアップ メニューからの選択が行えるようにします

ClientToScreen()

BOOL ClientToScreen(HWND hWnd , LPPOINT lpPoint);

クライアント座標をスクリーン座標に変換します

hWnd - 変換するクライアント座標のウィンドウのハンドルを指定します
lpPoing - 変換する座標が格納されている POINT 構造体へのポインタ

戻り値 - 成功すれば 0 以外、失敗すれば 0

ScreenToClient()

BOOL ScreenToClient(HWND hWnd , LPPOINT lpPoint);

スクリーン座標をクライアント座標に変換します

hWnd - 変換するクライアント座標のウィンドウのハンドルを指定します
lpPoing - 変換する座標が格納されている POINT 構造体へのポインタ

戻り値 - 成功すれば 0 以外、失敗すれば 0



前のページへ戻る次のページへ