Go Premium for a chance to win a PS4. Enter to Win

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 1634
  • Last Modified:

Setting the font for a windows title bar

Is there an easy way to set the font used in a window's title bar.  I do not want to change the overall system properties, just the font in certain windows.  I would prefer to avoid using non-client draw messages.

Any suggestions?
0
jstolan
Asked:
jstolan
1 Solution
 
BelgaratCommented:
Sorry, it seems that there is not a message/system call that would change font in window's caption. Try sending WM_SETFONT, but I suppose that only standard window classes (controls & dialog boxes) will accept it.
The only way I think about is to handle WM_NCPAINT in your program.
0
 
topolCommented:
I can send you the code, but it's in Visual Basic 5, but it uses API -- so you will easily convert it to C
OK?
0
 
jstolanAuthor Commented:
To topol

If I can adapt it, I will gladly give you the points.  My E-mail is

jstolan@ossconsulting.com

PS to Belgarat WM_SETFONT doesn't work
0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
BelgaratCommented:
topol: can you send me your code too ? e-mail is svatopluk.dedic@st.mff.cuni.cz.
Thanks in advance :-)
0
 
shaigCommented:
topol: can you send me your code too ? e-mail is shaig@inter.net.il
Thanks in advance :-)
0
 
piano_boxerCommented:
Here is some MFC code for drawing you own titlebar:
---------------------------------------------------

BOOL g_fDrawFancyCaption = TRUE;

/////////////////
// Paint non-client area: First let Windows paint, then I'll paint over it.
//
// I use the relatively obscure and poorly documented update region feature
// to prevent Windows from painting the title text. This is to get totally
// flicker-free painting when the user sizes the window. Note that Windows
// sometimes uses WPARAM=1.
//

void CMainFrame::OnNcPaint()
{
    if(!g_fDrawFancyCaption)
    {
        CMDIFrameWnd::OnNcPaint();
        return;
    }

    MSG& msg = AfxGetThreadState()->m_lastSentMsg;
    HRGN hRgn = (HRGN)msg.wParam;

    CCaptionRect rc(*this);     // caption rectangle in window coords
    CRect rcWin;                    // window rect
    GetWindowRect(&rcWin);      // .. get window rect
    rc += rcWin.TopLeft();      // convert caption rect to screen coords

    // Don't bother painting if the caption doesn't lie within the region.
    //
    if (msg.wParam > 1 && !::RectInRegion((HRGN)msg.wParam, &rc)) {
        CFrameWnd::OnNcPaint(); // just do default thing
        return;                     // and quit
    }

    // Exclude caption from update region
    //
    HRGN hRgnCaption = ::CreateRectRgnIndirect(&rc);
    HRGN hRgnNew     = ::CreateRectRgnIndirect(&rc);
    if (msg.wParam > 1) {
        // wParam is a valid region: subtract caption from it
        ::CombineRgn(hRgnNew, hRgn, hRgnCaption, RGN_DIFF);
    } else {
        // wParam is not a valid region: create one that's the whole
        // window minus the caption bar
        HRGN hRgnAll = ::CreateRectRgnIndirect(&rcWin);
        CombineRgn(hRgnNew, hRgnAll, hRgnCaption, RGN_DIFF);
        DeleteObject(hRgnAll);
    }

    // Call Windows to do WM_NCPAINT with altered update region
    //
    WPARAM savewp = msg.wParam; // save original wParam
    msg.wParam = (WPARAM)hRgnNew;   // set new region for DefWindowProc
    CFrameWnd::OnNcPaint();         // call it
    DeleteObject(hRgnCaption);      // clean up
    DeleteObject(hRgnNew);          // ...
    msg.wParam = savewp;                // restore original wParam

    PaintCaption();                 // Now paint my special caption
}

BOOL CMainFrame::OnNcActivate(BOOL bActive)
{
    if(!g_fDrawFancyCaption)
        return CMDIFrameWnd::OnNcActivate(bActive);


    // Mimic MFC kludge to stay active if WF_STAYACTIVE bit is on
    //
    if (m_nFlags & WF_STAYACTIVE)
        bActive = TRUE;
    if (!IsWindowEnabled())         // but not if disabled
        bActive = FALSE;

    if (bActive==m_bActive)
        return TRUE;                    // nothing to do

    DefWindowProc(WM_NCACTIVATE, bActive, 0L);

    m_bActive = bActive;        // update state
    SendMessage(WM_NCPAINT);    // paint non-client area (frame too)
   
    return TRUE;                    // done OK
}


void CMainFrame::OnSettingChange(UINT uFlags, LPCTSTR lpszSection)
{
    OnSysColorChange();
}

void CMainFrame::OnSysColorChange()
{
    if(!g_fDrawFancyCaption)
    {
        CMDIFrameWnd::OnSysColorChange();
        return;
    }

    m_fontCaption.DeleteObject();   // generate new fonts
    m_rcCaption.SetRectEmpty(); // will force new bitmap to be created
}


//////////////////
// Paint custom caption. Flag tells whether active or not.
// Just blast the bitmap to the title bar, but not iif minimized (iconic).
//
void CMainFrame::PaintCaption(BOOL bActive)
{
    if (!IsIconic())
    {
        CWindowDC dcWin(this);
        CDC dc;
        dc.CreateCompatibleDC(&dcWin);
        CBitmap* pOldBitmap = dc.SelectObject(GetCaptionBitmap(bActive));
        const CRect& rc = m_rcCaption;
        dcWin.BitBlt(rc.left,rc.top,rc.Width(),rc.Height(),&dc,0,0,SRCCOPY);
    }
}

//////////////////
// Helper function to compute the luminosity for an RGB color.
// Measures how bright the color is. I use this so I can draw the caption
// text using the user's chosen color, unless it's too dark. See MSDN for
// definition of luminosity and how to compute it.
//
static int GetLuminosity(COLORREF color)
{
#define HLSMAX 240  // This is what Display Properties uses
#define RGBMAX 255  // max r/g/b value is 255
    int r = GetRValue(color);
    int g = GetGValue(color);
    int b = GetBValue(color);
    int rgbMax = max( max(r,g), b);
    int rgbMin = min( min(r,g), b);
    return (((rgbMax+rgbMin) * HLSMAX) + RGBMAX ) / (2*RGBMAX);
}

#define COLOR_WHITE RGB(255,255,255)
#define COLOR_BLACK RGB(0,0,0)
#define NCOLORSHADES 64     // this many shades in gradient

//////////////////
// Helper to paint rectangle with a color.
//
static void PaintRect(CDC& dc, int x, int y, int w, int h, COLORREF color)
{
    CBrush brush(color);
    CBrush* pOldBrush = dc.SelectObject(&brush);
    dc.PatBlt(x, y, w, h, PATCOPY);
    dc.SelectObject(pOldBrush);
}

//////////////////
// Get the appropriate bitmap whether active/inactive. If the size of
// the caption rect has changed since the last time it was generated,
// generate it again.
//
// This is the function that actually does the shading. It creates a
// bitmap that's used to paint the caption. It looks horrible, but it's
// just a lot of bit-twiddling GDI stuff.
//
CBitmap* CMainFrame::GetCaptionBitmap(BOOL bActive)
{
    CBitmap& bm = m_bmCaption[bActive!=0]; // one of two bitmaps
    CCaptionRect rcCap(*this);                  // caption rectangle in win coords
    if (rcCap != m_rcCaption) {             // if changed since last time:
        m_bmCaption[0].DeleteObject();      // delete both bitmaps,
        m_bmCaption[1].DeleteObject();      // ..they are bad
        m_rcCaption = rcCap;                        // note for next time
    }
   
    if (bm.m_hObject)
        return &bm;                                // already have bitmap; return it.
   
    // Either the size changed or the bitmap was never created. Create it.
    //
    rcCap -= rcCap.TopLeft(); // convert caption rectangle origin to (0,0)
    int w = rcCap.Width();
    int h = rcCap.Height();
    int cxIcon = GetSystemMetrics(SM_CXSIZE);
    int cyIcon = GetSystemMetrics(SM_CYSIZE);

    // Create bitmap same size as caption area and select into memory DC
    //
    CWindowDC dcWin(this);
    CDC dc;
    dc.CreateCompatibleDC(&dcWin);
    bm.DeleteObject();
    bm.CreateCompatibleBitmap(&dcWin, w, h);
    CBitmap* pOldBitmap = dc.SelectObject(&bm);

    // Paint shaded background. Note all painting is into memory DC,
    // not window, so there's no flicker.
    //
    if (!bActive) {
        // Inactive caption: don't do shading, just fill w/bg color
        PaintRect(dc, 0, 0, w, h, GetSysColor(COLOR_INACTIVECAPTION));

    } else {
        // Active caption: do shading
        //
        COLORREF clrBG = GetSysColor(COLOR_ACTIVECAPTION); // background color
        int r = GetRValue(clrBG);               // red..
        int g = GetGValue(clrBG);               // ..green
        int b = GetBValue(clrBG);               // ..blue color vals
        int x = 5*rcCap.right/6;                // start 5/6 of the way right
        int w = x - rcCap.left;                 // width of area to shade
        int xDelta= max(w/NCOLORSHADES,1);  // width of one shade band

        // Paint far right 1/6 of caption the background color
        PaintRect(dc, x, 0, rcCap.right-x, h, clrBG);

        // Compute new color brush for each band from x to x + xDelta.
        // Excel uses a linear algorithm from black to normal, i.e.
        //
        //      color = CaptionColor * r
        //
        // where r is the ratio x/w, which ranges from 0 (x=0, left)
        // to 1 (x=w, right). This results in a mostly black title bar,
        // since we humans don't distinguish dark colors as well as light
        // ones. So instead, I use the formula
        //
        //      color = CaptionColor * [1-(1-r)^2]
        //
        // which still equals black when r=0 and CaptionColor when r=1,
        // but spends more time near CaptionColor. For example, when r=.5,
        // the multiplier is [1-(1-.5)^2] = .75, closer to 1 than .5.
        // I leave the algebra to the reader to verify that the above formula
        // is equivalent to
        //
        //      color = CaptionColor - (CaptionColor*(w-x)*(w-x))/(w*w)
        //
        // The computation looks horrendous, but it's only done once each
        // time the caption changes size; thereafter BitBlt'ed to the screen.
        //
        while (x > xDelta) {                        // paint bands right to left
            x -= xDelta;                            // next band
            int wmx2 = (w-x)*(w-x);             // w minus x squared
            int w2  = w*w;                          // w squared
            PaintRect(dc, x, 0, xDelta, h,  
                RGB(r-(r*wmx2)/w2, g-(g*wmx2)/w2, b-(b*wmx2)/w2));
        }

        PaintRect(dc,0,0,x,h,COLOR_BLACK);  // whatever's left ==> black
    }

    // Draw icon and caption buttons.
    // These are all drawn inside a rectangle of dimensions SM_C[XY]SIZE.
    //
    CRect rcButn(0,0,cxIcon,cyIcon);

    // Within the basic button rectangle, Windows 95 uses a 1 or 2 pixel border
    // Icon has 2 pixel border on left, 1 pixel on top/bottom, 0 right
    rcButn.DeflateRect(0,1);
    rcButn.left += 2;
    DrawIconEx(dc.m_hDC, rcButn.left, rcButn.top,
        (HICON)GetClassLong(m_hWnd, GCL_HICONSM),
        rcButn.Width(), rcButn.Height(), 0, NULL, DI_NORMAL);


    // Close box has a 2 pixel border on all sides but left, which is zero
    rcButn.DeflateRect(0,1);                // shrink top/bottom 1 more
    rcButn += CPoint(w-cxIcon-2,0);     // move right
    dc.DrawFrameControl(&rcButn, DFC_CAPTION, DFCS_CAPTIONCLOSE);


    DWORD dwStyle = GetStyle();

    // Max/restore button is like close box; just shift rectangle left
    //
    if (dwStyle & WS_MAXIMIZEBOX) {
        rcButn -= CPoint(cxIcon,0);
        dc.DrawFrameControl(&rcButn, DFC_CAPTION,
            IsZoomed() ? DFCS_CAPTIONRESTORE : DFCS_CAPTIONMAX);
    }

    // Minimize button has 2 pixel border on all sides but right.
    //
    if (dwStyle & WS_MINIMIZEBOX) {
        rcButn -= CPoint(cxIcon-2,0);
        dc.DrawFrameControl(&rcButn, DFC_CAPTION, DFCS_CAPTIONMIN);
    }


    // Now draw text. First Create fonts if needed
    //
    if (!m_fontCaption.m_hObject)
        CreateFonts();
    CFont* pOldFont = (CFont*)dc.SelectObject(&m_fontCaption);

   
    // Paint "ACME TEXT" using ACME font, always white
    //
    CRect rcText = rcCap;                    // caption rectangle
    rcText.left += cxIcon+2;                 // start after icon
    rcText.right = rcButn.left-2;            // don't draw past buttons
    dc.SetBkMode(TRANSPARENT);



    // Now paint window title (caption)
    //
    if (rcText.right > rcText.left)
    {       // if still room:
        COLORREF clrText;                           // text color
        if (bActive) {
            // Excel always uses white for title color, but I use the user's
            // selected color--unless it's too dark, then I use white.
            //
            clrText = GetSysColor(COLOR_CAPTIONTEXT);
            if (GetLuminosity(clrText) < 90) // good from trial & error
                clrText = COLOR_WHITE;
        } else
            clrText = GetSysColor(COLOR_INACTIVECAPTIONTEXT);

        // Paint the text. Use DT_END_ELLIPSIS to draw ellipsis if text
        // won't fit. Win32 sure is friendly!
        //
        dc.SetTextColor(clrText);
        CString s;
        GetWindowText(s);
        dc.DrawText(s, &rcText, DT_LEFT|DT_VCENTER|DT_SINGLELINE|DT_END_ELLIPSIS);
    }


    // Restore DC
    dc.SelectObject(pOldFont);
    dc.SelectObject(pOldBitmap);

    return &bm; // return bitmap to caller--whew!
}

//////////////////
// Helper function to build the fonts I need.
//
void CMainFrame::CreateFonts()
{
    // Get current system caption font, just to get its size
    //
    NONCLIENTMETRICS ncm;
    ncm.cbSize = sizeof(ncm);
    VERIFY(SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0));
    m_fontCaption.CreateFontIndirect(&ncm.lfCaptionFont);
}

//////////////////
// CCaptionRect: Constructor conputes caption rectangle in window coords.
//
CCaptionRect::CCaptionRect(const CWnd& wnd)
{
    // Get size of frame around window
    DWORD dwStyle = wnd.GetStyle();
    CSize szFrame = (dwStyle & WS_THICKFRAME) ?
        CSize(GetSystemMetrics(SM_CXSIZEFRAME),
               GetSystemMetrics(SM_CYSIZEFRAME)) :
        CSize(GetSystemMetrics(SM_CXFIXEDFRAME),
                GetSystemMetrics(SM_CYFIXEDFRAME));

    int cxIcon = GetSystemMetrics(SM_CXSIZE); // width of caption icon/button

    // Compute rectangle
    wnd.GetWindowRect(this);        // window rect in screen coords
    *this -= CPoint(left, top); // shift origin to (0,0)
    left  += szFrame.cx;                // frame
    right -= szFrame.cx;                // frame
    top   += szFrame.cy;                // top = end of frame
    bottom = top + GetSystemMetrics(SM_CYCAPTION)  // height of caption
        - GetSystemMetrics(SM_CYBORDER);                  // minus gray shadow border
}

LRESULT CMainFrame::OnSetText(WPARAM wParam, LPARAM lParam)
{
    if(!g_fDrawFancyCaption)
        return Default();

    // Turn WS_VISIBLE style off before calling Windows to
    // set the text, then turn it back on again after.
    //
    DWORD dwStyle = GetStyle();
    if (dwStyle & WS_VISIBLE)
        SetWindowLong(m_hWnd, GWL_STYLE, dwStyle & ~ WS_VISIBLE);
    LRESULT lRet = DefWindowProc(WM_SETTEXT, wParam, lParam);
    if (dwStyle & WS_VISIBLE)
        SetWindowLong(m_hWnd, GWL_STYLE, dwStyle);

    m_rcCaption.SetRectEmpty();          // invalidate caption
    SendMessage(WM_NCPAINT);    // paint non-client area (frame too)
    PaintCaption();                          // ..and paint it
    return lRet;
}

0
 
jstolanAuthor Commented:
Thanks piano_boxer, that's just what I was looking for.  I must admit it was a little more complicated than I thought.  I was able to simplify your sample quite a bit, as all I really needed was to change the font so Kanji characters would be guaranteed to be displayed.

Topol, I got your E-mail.  But in fairness, piano-boxer's example was more appropriate since it was in MFC.  If you would like some points, leave a message here and I will put up a special question for you.
0

Featured Post

Important Lessons on Recovering from Petya

In their most recent webinar, Skyport Systems explores ways to isolate and protect critical databases to keep the core of your company safe from harm.

Tackle projects and never again get stuck behind a technical roadblock.
Join Now