comctl32: Add basic custom draw support for tooltips.
This commit is contained in:
parent
825baa23a2
commit
f54570fdbd
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright 2005 Dmitry Timoshkov
|
||||
* Copyright 2008 Jason Edmeades
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
|
@ -60,9 +61,181 @@ static void test_create_tooltip(void)
|
|||
DestroyWindow(parent);
|
||||
}
|
||||
|
||||
/* try to make sure pending X events have been processed before continuing */
|
||||
static void flush_events(int waitTime)
|
||||
{
|
||||
MSG msg;
|
||||
int diff = waitTime;
|
||||
DWORD time = GetTickCount() + waitTime;
|
||||
|
||||
while (diff > 0)
|
||||
{
|
||||
if (MsgWaitForMultipleObjects( 0, NULL, FALSE, min(100,diff), QS_ALLEVENTS) == WAIT_TIMEOUT) break;
|
||||
while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
|
||||
diff = time - GetTickCount();
|
||||
}
|
||||
}
|
||||
|
||||
static int CD_Stages;
|
||||
static LRESULT CD_Result;
|
||||
static HWND g_hwnd;
|
||||
|
||||
#define TEST_CDDS_PREPAINT 0x00000001
|
||||
#define TEST_CDDS_POSTPAINT 0x00000002
|
||||
#define TEST_CDDS_PREERASE 0x00000004
|
||||
#define TEST_CDDS_POSTERASE 0x00000008
|
||||
#define TEST_CDDS_ITEMPREPAINT 0x00000010
|
||||
#define TEST_CDDS_ITEMPOSTPAINT 0x00000020
|
||||
#define TEST_CDDS_ITEMPREERASE 0x00000040
|
||||
#define TEST_CDDS_ITEMPOSTERASE 0x00000080
|
||||
#define TEST_CDDS_SUBITEM 0x00000100
|
||||
|
||||
static LRESULT CALLBACK CustomDrawWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch(msg) {
|
||||
|
||||
case WM_DESTROY:
|
||||
PostQuitMessage(0);
|
||||
break;
|
||||
|
||||
case WM_NOTIFY:
|
||||
if (((NMHDR *)lParam)->code == NM_CUSTOMDRAW) {
|
||||
NMTTCUSTOMDRAW *ttcd = (NMTTCUSTOMDRAW*) lParam;
|
||||
ok(ttcd->nmcd.hdr.hwndFrom == g_hwnd, "Unexpected hwnd source %x (%x)\n",
|
||||
(int)ttcd->nmcd.hdr.hwndFrom, (int) g_hwnd);
|
||||
ok(ttcd->nmcd.hdr.idFrom == 0x1234ABCD, "Unexpected id %x\n", (int)ttcd->nmcd.hdr.idFrom);
|
||||
|
||||
switch (ttcd->nmcd.dwDrawStage) {
|
||||
case CDDS_PREPAINT : CD_Stages |= TEST_CDDS_PREPAINT; break;
|
||||
case CDDS_POSTPAINT : CD_Stages |= TEST_CDDS_POSTPAINT; break;
|
||||
case CDDS_PREERASE : CD_Stages |= TEST_CDDS_PREERASE; break;
|
||||
case CDDS_POSTERASE : CD_Stages |= TEST_CDDS_POSTERASE; break;
|
||||
case CDDS_ITEMPREPAINT : CD_Stages |= TEST_CDDS_ITEMPREPAINT; break;
|
||||
case CDDS_ITEMPOSTPAINT: CD_Stages |= TEST_CDDS_ITEMPOSTPAINT; break;
|
||||
case CDDS_ITEMPREERASE : CD_Stages |= TEST_CDDS_ITEMPREERASE; break;
|
||||
case CDDS_ITEMPOSTERASE: CD_Stages |= TEST_CDDS_ITEMPOSTERASE; break;
|
||||
case CDDS_SUBITEM : CD_Stages |= TEST_CDDS_SUBITEM; break;
|
||||
default: CD_Stages = -1;
|
||||
}
|
||||
|
||||
if (ttcd->nmcd.dwDrawStage == CDDS_PREPAINT) return CD_Result;
|
||||
}
|
||||
/* drop through */
|
||||
|
||||
default:
|
||||
return DefWindowProcA(hWnd, msg, wParam, lParam);
|
||||
}
|
||||
|
||||
return 0L;
|
||||
}
|
||||
|
||||
static void test_customdraw(void) {
|
||||
static struct {
|
||||
LRESULT FirstReturnValue;
|
||||
int ExpectedCalls;
|
||||
} expectedResults[] = {
|
||||
/* Valid notification responses */
|
||||
{CDRF_DODEFAULT, TEST_CDDS_PREPAINT},
|
||||
{CDRF_SKIPDEFAULT, TEST_CDDS_PREPAINT},
|
||||
{CDRF_NOTIFYPOSTPAINT, TEST_CDDS_PREPAINT | TEST_CDDS_POSTPAINT},
|
||||
|
||||
/* Invalid notification responses */
|
||||
{CDRF_NOTIFYITEMDRAW, TEST_CDDS_PREPAINT},
|
||||
{CDRF_NOTIFYPOSTERASE, TEST_CDDS_PREPAINT},
|
||||
{CDRF_NOTIFYSUBITEMDRAW, TEST_CDDS_PREPAINT},
|
||||
{CDRF_NEWFONT, TEST_CDDS_PREPAINT}
|
||||
};
|
||||
|
||||
int iterationNumber;
|
||||
WNDCLASSA wc;
|
||||
LRESULT lResult;
|
||||
|
||||
/* Create a class to use the custom draw wndproc */
|
||||
wc.style = CS_HREDRAW | CS_VREDRAW;
|
||||
wc.cbClsExtra = 0;
|
||||
wc.cbWndExtra = 0;
|
||||
wc.hInstance = GetModuleHandleA(NULL);
|
||||
wc.hIcon = NULL;
|
||||
wc.hCursor = LoadCursorA(NULL, IDC_ARROW);
|
||||
wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
|
||||
wc.lpszMenuName = NULL;
|
||||
wc.lpszClassName = "CustomDrawClass";
|
||||
wc.lpfnWndProc = CustomDrawWndProc;
|
||||
RegisterClass(&wc);
|
||||
|
||||
for (iterationNumber = 0;
|
||||
iterationNumber < sizeof(expectedResults)/sizeof(expectedResults[0]);
|
||||
iterationNumber++) {
|
||||
|
||||
HWND parent, hwndTip;
|
||||
TOOLINFO toolInfo = { 0 };
|
||||
|
||||
/* Create a main window */
|
||||
parent = CreateWindowEx(0, "CustomDrawClass", NULL,
|
||||
WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX |
|
||||
WS_MAXIMIZEBOX | WS_VISIBLE,
|
||||
50, 50,
|
||||
300, 300,
|
||||
NULL, NULL, NULL, 0);
|
||||
ok(parent != NULL, "Creation of main window failed\n");
|
||||
|
||||
/* Make it show */
|
||||
ShowWindow(parent, SW_SHOWNORMAL);
|
||||
flush_events(100);
|
||||
|
||||
/* Create Tooltip */
|
||||
hwndTip = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS,
|
||||
NULL, TTS_NOPREFIX | TTS_ALWAYSTIP,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
parent, NULL, GetModuleHandleA(NULL), 0);
|
||||
ok(hwndTip != NULL, "Creation of tooltip window failed\n");
|
||||
|
||||
/* Set up parms for the wndproc to handle */
|
||||
CD_Stages = 0;
|
||||
CD_Result = expectedResults[iterationNumber].FirstReturnValue;
|
||||
g_hwnd = hwndTip;
|
||||
|
||||
/* Make it topmost, as per the MSDN */
|
||||
SetWindowPos(hwndTip, HWND_TOPMOST, 0, 0, 0, 0,
|
||||
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
|
||||
|
||||
/* Create a tool */
|
||||
toolInfo.cbSize = sizeof(TOOLINFO);
|
||||
toolInfo.hwnd = parent;
|
||||
toolInfo.hinst = GetModuleHandleA(NULL);
|
||||
toolInfo.uFlags = TTF_SUBCLASS;
|
||||
toolInfo.uId = (UINT_PTR)0x1234ABCD;
|
||||
toolInfo.lpszText = (LPSTR)"This is a test tooltip";
|
||||
toolInfo.lParam = 0xdeadbeef;
|
||||
GetClientRect (parent, &toolInfo.rect);
|
||||
lResult = SendMessage(hwndTip, TTM_ADDTOOL, 0, (LPARAM)&toolInfo);
|
||||
ok(lResult, "Adding the tool to the tooltip failed\n");
|
||||
|
||||
/* Make tooltip appear quickly */
|
||||
SendMessage(hwndTip, TTM_SETDELAYTIME, (WPARAM)TTDT_INITIAL, (LPARAM)MAKELONG(1,0));
|
||||
|
||||
/* Put cursor inside window, tooltip will appear immediately */
|
||||
SetCursorPos(100, 100);
|
||||
flush_events(200);
|
||||
|
||||
/* Check CustomDraw results */
|
||||
ok(CD_Stages == expectedResults[iterationNumber].ExpectedCalls,
|
||||
"CustomDraw run %d stages %x, expected %x\n", iterationNumber, CD_Stages,
|
||||
expectedResults[iterationNumber].ExpectedCalls);
|
||||
|
||||
/* Clean up */
|
||||
DestroyWindow(hwndTip);
|
||||
DestroyWindow(parent);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
START_TEST(tooltips)
|
||||
{
|
||||
InitCommonControls();
|
||||
|
||||
test_create_tooltip();
|
||||
test_customdraw();
|
||||
}
|
||||
|
|
|
@ -202,6 +202,44 @@ TOOLTIPS_InitSystemSettings (TOOLTIPS_INFO *infoPtr)
|
|||
infoPtr->hTitleFont = CreateFontIndirectW (&nclm.lfStatusFont);
|
||||
}
|
||||
|
||||
/* Custom draw routines */
|
||||
static void
|
||||
TOOLTIPS_customdraw_fill(NMTTCUSTOMDRAW *lpnmttcd,
|
||||
const HWND hwnd,
|
||||
HDC hdc, const RECT *rcBounds, UINT uFlags)
|
||||
{
|
||||
TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr(hwnd);
|
||||
|
||||
ZeroMemory(lpnmttcd, sizeof(NMTTCUSTOMDRAW));
|
||||
lpnmttcd->uDrawFlags = uFlags;
|
||||
lpnmttcd->nmcd.hdr.hwndFrom = hwnd;
|
||||
lpnmttcd->nmcd.hdr.code = NM_CUSTOMDRAW;
|
||||
if (infoPtr->nCurrentTool != -1) {
|
||||
TTTOOL_INFO *toolPtr = &infoPtr->tools[infoPtr->nCurrentTool];
|
||||
lpnmttcd->nmcd.hdr.idFrom = toolPtr->uId;
|
||||
}
|
||||
lpnmttcd->nmcd.hdc = hdc;
|
||||
lpnmttcd->nmcd.rc = *rcBounds;
|
||||
/* FIXME - dwItemSpec, uItemState, lItemlParam */
|
||||
}
|
||||
|
||||
static inline DWORD
|
||||
TOOLTIPS_notify_customdraw (DWORD dwDrawStage, NMTTCUSTOMDRAW *lpnmttcd)
|
||||
{
|
||||
LRESULT result = CDRF_DODEFAULT;
|
||||
lpnmttcd->nmcd.dwDrawStage = dwDrawStage;
|
||||
|
||||
TRACE("Notifying stage %d, flags %x, id %x\n", lpnmttcd->nmcd.dwDrawStage,
|
||||
lpnmttcd->uDrawFlags, lpnmttcd->nmcd.hdr.code);
|
||||
|
||||
result = SendMessageW(GetParent(lpnmttcd->nmcd.hdr.hwndFrom), WM_NOTIFY,
|
||||
0, (LPARAM)lpnmttcd);
|
||||
|
||||
TRACE("Notify result %x\n", (unsigned int)result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
TOOLTIPS_Refresh (HWND hwnd, HDC hdc)
|
||||
{
|
||||
|
@ -213,6 +251,8 @@ TOOLTIPS_Refresh (HWND hwnd, HDC hdc)
|
|||
UINT uFlags = DT_EXTERNALLEADING;
|
||||
HRGN hRgn = NULL;
|
||||
DWORD dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
|
||||
NMTTCUSTOMDRAW nmttcd;
|
||||
DWORD cdmode;
|
||||
|
||||
if (infoPtr->nMaxTipWidth > -1)
|
||||
uFlags |= DT_WORDBREAK;
|
||||
|
@ -224,6 +264,13 @@ TOOLTIPS_Refresh (HWND hwnd, HDC hdc)
|
|||
|
||||
oldBkMode = SetBkMode (hdc, TRANSPARENT);
|
||||
SetTextColor (hdc, infoPtr->clrText);
|
||||
hOldFont = SelectObject (hdc, infoPtr->hFont);
|
||||
|
||||
/* Custom draw - Call PrePaint once initial properties set up */
|
||||
/* Note: Contrary to MSDN, CDRF_SKIPDEFAULT still draws a tooltip */
|
||||
TOOLTIPS_customdraw_fill(&nmttcd, hwnd, hdc, &rc, uFlags);
|
||||
cdmode = TOOLTIPS_notify_customdraw(CDDS_PREPAINT, &nmttcd);
|
||||
uFlags = nmttcd.uDrawFlags;
|
||||
|
||||
if (dwStyle & TTS_BALLOON)
|
||||
{
|
||||
|
@ -259,6 +306,7 @@ TOOLTIPS_Refresh (HWND hwnd, HDC hdc)
|
|||
RECT rcTitle = {rc.left, rc.top, rc.right, rc.bottom};
|
||||
int height;
|
||||
BOOL icon_present;
|
||||
HFONT prevFont;
|
||||
|
||||
/* draw icon */
|
||||
icon_present = infoPtr->hTitleIcon &&
|
||||
|
@ -270,9 +318,9 @@ TOOLTIPS_Refresh (HWND hwnd, HDC hdc)
|
|||
rcTitle.bottom = rc.top + ICON_HEIGHT;
|
||||
|
||||
/* draw title text */
|
||||
hOldFont = SelectObject (hdc, infoPtr->hTitleFont);
|
||||
prevFont = SelectObject (hdc, infoPtr->hTitleFont);
|
||||
height = DrawTextW(hdc, infoPtr->pszTitle, -1, &rcTitle, DT_BOTTOM | DT_SINGLELINE | DT_NOPREFIX);
|
||||
SelectObject (hdc, hOldFont);
|
||||
SelectObject (hdc, prevFont);
|
||||
rc.top += height + BALLOON_TITLE_TEXT_SPACING;
|
||||
}
|
||||
}
|
||||
|
@ -286,8 +334,13 @@ TOOLTIPS_Refresh (HWND hwnd, HDC hdc)
|
|||
}
|
||||
|
||||
/* draw text */
|
||||
hOldFont = SelectObject (hdc, infoPtr->hFont);
|
||||
DrawTextW (hdc, infoPtr->szTipText, -1, &rc, uFlags);
|
||||
|
||||
/* Custom draw - Call PostPaint after drawing */
|
||||
if (cdmode & CDRF_NOTIFYPOSTPAINT) {
|
||||
TOOLTIPS_notify_customdraw(CDDS_POSTPAINT, &nmttcd);
|
||||
}
|
||||
|
||||
/* be polite and reset the things we changed in the dc */
|
||||
SelectObject (hdc, hOldFont);
|
||||
SetBkMode (hdc, oldBkMode);
|
||||
|
|
Loading…
Reference in New Issue