ツールチップ


ツールヒント

実践レベルのアプリケーションを開発すると、大抵多くのコントロールでウィンドウが埋まります
沢山あるコントロールを、いかに直感的に理解しやすく設計するかという問題は
GUI アプリケーションの永遠の課題といえるでしょう

ツールチップは、コントロールの使い方や意味を解説する代表的な手段です
ステータスバーも同様のことが可能ですが、ステータスバーと異なり
ツールチップはウィンドウの領域を無駄にすることなく、スマートに使うことができます

ツールチップを表示させるには、まずツールヒントコントロールを生成します
ツールヒントコントロールは CreateWindowEx() 関数を用いて作成します
このとき、第2引数で指定するクラス名は TOOLTIPS_CLASS を指定します

ウィンドウサイズは CW_USEDEFAULT を指定してください
ウィンドウスタイルには TTS_ALWAYSTIPTTS_NOPREFIX が指定できます
TTS_ALWAYSTIP は、ウィンドウがアクティブでなくてもツールチップを表示します
TTS_NOPREFIX を指定すれば & 記号も削除されることなく表示されます
ツールチップでは、メニュー項目の仕様に合わせて、デフォルトで & 記号は削除されます

CreateWindowEx() が成功すれば、ツールヒントコントロールのハンドルが返ります
チップを表示するには、このハンドルにツールを登録しなければいけません

ツールチップをツールヒントコントロールに登録するには
ツールヒントコントロールに TTM_ADDTOOL メッセージを送信します
このメッセージの WPARAM は常に 0 を指定します
LPARAM には、チップの情報を格納した TOOLINFO 構造体へのポインタを指定します
TOOLINFO 構造体は、次のように定義されています
typedef struct tagTOOLINFO{
    UINT      cbSize; 
    UINT      uFlags; 
    HWND      hwnd; 
    UINT      uId; 
    RECT      rect; 
    HINSTANCE hinst; 
    LPTSTR    lpszText; 
} TOOLINFO, NEAR *PTOOLINFO, FAR *LPTOOLINFO;
cbSize には、この構造体のサイズを指定します
uFlags はツールチップに関する次の定数を組み合わせて指定できます

定数解説
TTF_CENTERTIP uId メンバで指定されたツールのした中央に
ツールヒントウィンドウを表示する
TTF_IDISHWND uId メンバは、ツールのウィンドウハンドルである
このフラグが設定されていない場合、uId はツールの識別子である
TTF_RTLREADING テキストを右から左に表示する(ヘブライ語、またはアラビア語)
TTF_SUBCLASS ツールのウィンドウをサブクラス化し
ツールヒントコントロールにマウスメッセージを送信する
これが指定されていない場合、TTM_RELAYEVENT メッセージを
ツールヒントコントロールに送信しなければならない

hwnd には、ツールチップを表示するウィンドウのハンドルを指定します
uId には、ツールチップの識別子を指定します
uFlags メンバに TTF_IDISHWND が指定されている場合は、ウィンドウのハンドルを指定します

rect には、hwnd のクライアント領域の左上を原点とし
ツールチップの表示が有効な境界矩形の座標を指定します

hinst は、ツールの文字列リソースが格納されているモジュールのインスタンスを指定します
これを指定する場合、lpszText は文字列リソースの識別子を指定します
hinst が NULL であれば、lpszText には、ツールチップが表示する文字列を指定します

lpszText に LPSTR_TEXTCALLBACK という値を指定すると
ツールヒントコントロールが親ウィンドウに TTN_NEETTEXT メッセージを送信し
このメッセージを処理してツールチップを表示するという処理方法になります
この方法については、この場では説明しません

uFlags メンバには、一般的に TTF_SUBCLASS を指定します
これを指定しなければ、マウスメッセージをツールに送信する必要があります
TTF_SUBCLASS を指定すれば、それらの処理をサブクラス化によって自動化できます

TTM_ADDTOOL メッセージの処理が成功すれば TRUE、失敗すれば FALSE が返ります
#include <windows.h>
#include <commctrl.h>
#define TITLE TEXT("Kitty on your lap")

LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
	static HWND hTool;
	static TOOLINFO ti;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		InitCommonControls();
		hTool = CreateWindowEx( 0 , TOOLTIPS_CLASS ,
			NULL , TTS_ALWAYSTIP ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			hWnd , NULL , ((LPCREATESTRUCT)(lp))->hInstance ,
			NULL
		);
		GetClientRect(hWnd , &ti.rect);

		ti.cbSize = sizeof (TOOLINFO);
		ti.uFlags = TTF_SUBCLASS;
		ti.hwnd = hWnd;
		ti.lpszText = TITLE;
		SendMessage(hTool , TTM_ADDTOOL , 0 , (LPARAM)&ti);
		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") , TITLE ,
			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;
}


このプログラムは、ウィンドウのクライアント領域にツールチップを表示します
クライアント領域で、1秒ほどカーソルを待機させると、自動的に表示されます

ただし、このプログラムには弱点があります
ツールチップの有効境界矩形が初期化時にしか指定されていないため
初期時よりウィンドウサイズを大きくすると、有効境界矩形をはみ出します
初期時の矩形よりも外の位置では、ツールチップが表示されることはありません

そこで、ウィンドウサイズが変更された場合
ツールチップの境界矩形も再設定する必要があると考えられます
境界矩形の変更には TTM_NEWTTOLRECT メッセージを使用します
WPARAM は常に 0、LPARAM に再設定する TOOLINFO 構造体へのポインタを指定します
この時、再設定するツールチップは TOOLINFO の uId メンバで判断されます
このメッセージに、戻り値はありません
#include <windows.h>
#include <commctrl.h>
#define TITLE TEXT("Kitty on your lap")

LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
	static HWND hTool;
	static TOOLINFO ti;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		InitCommonControls();
		hTool = CreateWindowEx( 0 , TOOLTIPS_CLASS ,
			NULL , TTS_ALWAYSTIP ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			hWnd , NULL , ((LPCREATESTRUCT)(lp))->hInstance ,
			NULL
		);
		GetClientRect(hWnd , &ti.rect);

		ti.cbSize = sizeof (TOOLINFO);
		ti.uFlags = TTF_SUBCLASS;
		ti.hwnd = hWnd;
		ti.uId = 1;
		ti.lpszText = TITLE;
		SendMessage(hTool , TTM_ADDTOOL , 0 , (LPARAM)&ti);
		return 0;
	case WM_SIZE:
		GetClientRect(hWnd , &ti.rect);
		SendMessage(hTool , TTM_NEWTOOLRECT , 0 , (LPARAM)&ti);
		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") , TITLE ,
			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;
}
先ほどのプログラムを改良したものです
今度は、クライアント領域の何処でも、どのサイズに変更してもチップが表示されるでしょう



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