Re: Drawing titlebar on XP with themes

From: John Carson (donaldquixote_at_datafast.net.au)
Date: 03/01/04


Date: Mon, 1 Mar 2004 16:53:01 +1100


"John Carson" <donaldquixote@datafast.net.au> wrote in message
news:eTqYuit$DHA.3256@TK2MSFTNGP09.phx.gbl
>
> 2. I let XP draw the caption (including buttons etc.) and then redraw
> the background and text in the relevant section of the title bar.
> This might cause flickering but no flickering is discernible on my
> system. I do make use of the clipping region supplied with WM_NCPAINT
> in order to eliminate what flickering was discernible on my system.
>

On closer examination, there is a flickering problem if the window is
rapidly resized. The revised version below adjusts the clipping region in
WM_NCPAINT so that Windows does not draw any text in the caption. Even with
this change, there was still minor flickering when the window was rapidly
resized, so I modified the code to draw into a memory DC and then BitBlt to
screen. The drawing is now flicker free.

I have also changed from using TextOut to using DrawText in order to clip
the window title when the window is too narrow to fit all of the window
title.

void MyDrawCaption(HWND hwnd, UINT message,
                            WPARAM wParam, LPARAM lParam)
{
HTHEME htheme = OpenThemeData(hwnd, L"window");
HDC hdc;
BOOL active;
RECT winRect, captionRect, clipRect;
int cxframe = GetSystemMetrics( SM_CXFRAME); // resizing frame Y border
int cyframe = GetSystemMetrics( SM_CYFRAME); // resizing frame X border
int cxbutton = GetSystemMetrics( SM_CXSIZE );// width of a caption button
int cxicon = GetSystemMetrics(SM_CXSMICON); // width of caption icon
int cxborder = GetSystemMetrics( SM_CXBORDER ); // generic window X border
int cyborder = GetSystemMetrics( SM_CYBORDER ); // generic window Y border

GetWindowRect( hwnd, (LPRECT)&winRect );
captionRect = winRect;
// convert to client coordinates
MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&captionRect, 2);
// set bottom of caption equal to top of client area
captionRect.bottom = 0;
// adjust so coordinates relate to window area, not client area
OffsetRect(&captionRect, -captionRect.left, -captionRect.top);

// Compute clipping rect giving the "owner-drawn" area and
// excluding the maximise button etc.
clipRect = captionRect;
clipRect.left += cxframe + cxborder + cxicon;
clipRect.right -= 3*cxbutton + 2*cxborder + cxframe;

// Have Windows draw the caption bar, clipping text
// drawing where possible.
if(message == WM_NCACTIVATE)
    DefWindowProc( hwnd, message, wParam, lParam );
else // WM_NCPAINT
{
    // winClipRect is the "owner-drawn" area in device coordinates
    RECT winClipRect = winRect;
    winClipRect.bottom = winClipRect.top + captionRect.bottom;
    winClipRect.left += cxframe + cxborder + cxicon;
    winClipRect.right -= 3*cxbutton + 2*cxborder + cxframe;

    HRGN hrgnInitial;
    if(wParam != 1)
        hrgnInitial = (HRGN)wParam;
    else // all of non-client area needs to be redrawn
        hrgnInitial = CreateRectRgnIndirect(&winRect);

    HRGN hrgnClip = CreateRectRgnIndirect(&winClipRect);
    CombineRgn(hrgnClip, hrgnInitial, hrgnClip, RGN_DIFF);
    DefWindowProc( hwnd, message, (WPARAM)hrgnClip, lParam );
    DeleteObject(hrgnClip);

    if(wParam == 1)
        DeleteObject(hrgnInitial);
}

// Get HDC for own drawing
if(message == WM_NCACTIVATE)
{
    active = ((BOOL)wParam == TRUE);
    hdc = GetWindowDC(hwnd);
}
else // WM_NCPAINT
{
    active = (GetActiveWindow()==hwnd);
    if(wParam != 1)
    {
        // wParam is a clipping region handle, but using it
        // directly causes misbehaviour in some cases, so we
        // use a copy, hrgnCopy. No need to delete it later.
        // The system does that.
        HRGN hrgnCopy = CreateRectRgn(0,0,1,1);
        CombineRgn(hrgnCopy, (HRGN)wParam, 0, RGN_COPY);
        hdc = GetDCEx(hwnd, hrgnCopy,
        DCX_CACHE | DCX_WINDOW | DCX_INTERSECTRGN);
    }
    else
        hdc = GetWindowDC(hwnd);
}

// Now do the "owner-draw" stuff. Draw into a memory DC
// to eliminate flickering
int width = captionRect.right-captionRect.left;
int height = captionRect.bottom-captionRect.top;
HDC hdcMem = CreateCompatibleDC(hdc);
HBITMAP hbitmap = CreateCompatibleBitmap(hdc, width, height);
HBITMAP hbitmapold = (HBITMAP)SelectObject(hdcMem, hbitmap);
// save DC for easy restoration
SaveDC(hdcMem);

// Draw the caption background
DrawThemeBackground(htheme, hdcMem, WP_CAPTION,
(active ? CS_ACTIVE : CS_INACTIVE), &captionRect, &clipRect);

// Retrieve system text color and set device context appropriately
UINT sysColor = active ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT;
COLORREF cr = GetThemeSysColor(htheme, sysColor);
SetTextColor(hdcMem, cr);
SetBkMode(hdcMem, TRANSPARENT);

TCHAR *str = _T("My Own Caption");
LOGFONT lf = {0};
_tcscpy(lf.lfFaceName, _T("Times New Roman"));
HFONT hfont = CreateFontIndirect(&lf);
SelectObject(hdcMem, hfont);
// Compute the rectangle in which the caption is to be drawn
RECT drawRect = clipRect;
drawRect.left = cxframe + cxborder + 3*cxicon/2;
drawRect.top = cyframe + cyborder;
DrawText(hdcMem, str, -1, &drawRect, DT_END_ELLIPSIS);

// BitBlt the completed owner draw section to the title bar

width = clipRect.right-clipRect.left;
height = clipRect.bottom-clipRect.top;
BitBlt(hdc, clipRect.left, clipRect.top, width,
    height, hdcMem, clipRect.left, clipRect.top, SRCCOPY);

// cleanup
RestoreDC(hdcMem, -1);
DeleteObject(hdcMem);
DeleteObject(hbitmap);
DeleteObject(hfont);
ReleaseDC(hwnd, hdc);
CloseThemeData(htheme);
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM
lParam)
{
    switch (message)
    {
    case WM_CREATE :
    {
        InitCommonControls();
        return 0;
    }
    case WM_NCACTIVATE:
    {
        MyDrawCaption(hwnd, message, wParam, lParam);
        return TRUE;
    }
    case WM_NCPAINT:
    {
        MyDrawCaption(hwnd, message, wParam, lParam);
        return 0;
    }
    case WM_DESTROY :
        PostQuitMessage (0) ;
        return 0 ;
    }
    return DefWindowProc (hwnd, message, wParam, lParam);
}

-- 
John Carson
1. To reply to email address, remove donald
2. Don't reply to email address (post here instead)