Link to home
Start Free TrialLog in
Avatar of mrwad99
mrwad99Flag for United Kingdom of Great Britain and Northern Ireland

asked on

Owner draw tab control: emboldened text problem

Ah hello.

I have an owner draw tab control where the active tab's title is displayed in a bold, blue text.  The inactive tab's text is displayed in basic black text.  I initialise the bold font in the constructor, and redraw each tab appropriately as below.

m_TabFont is a CFont member of CMyTabCtrl.

CMyTabCtrl::CMyTabCtrl()
{
      LOGFONT lf;
      memset(&lf, 0, sizeof(LOGFONT));                  // zero out structure
      lf.lfHeight = 12;                              // request a 12-pixel-height font
      lf.lfWeight = FW_BOLD;
      strcpy(lf.lfFaceName, "MS Sans Serif");                  // request a face name "Arial"
      VERIFY(m_TabFont.CreateFontIndirect(&lf));            // create the font

}

void CMyTabCtrl::DrawItem(LPDRAWITEMSTRUCT lpdis)
{  
      const COLORREF BLUE = RGB(2, 30, 157);
      const COLORREF BLACK = RGB(0, 0, 0);
      const COLORREF RED = RGB(255, 0, 0);
      const COLORREF WHITE = RGB(255, 255, 255);
      const COLORREF LIGHT_BLUE = RGB(0, 136, 136);

      CRect rect = lpdis->rcItem;

      int nTabIndex = lpdis->itemID;
      if (nTabIndex < 0) return;

      BOOL bSelected = (nTabIndex == GetCurSel());
      
      char label[64];
      TC_ITEM tci;
      tci.mask = TCIF_TEXT;
      tci.pszText = label;
      tci.cchTextMax = 63;
      
      // Get all the relevant data into the TC_ITEM structure...
      if (!GetItem(nTabIndex, &tci)) return;

      CDC* pDC = CDC::FromHandle(lpdis->hDC);
      if (!pDC) return;
      int nSavedDC = pDC->SaveDC();

      pDC->SetBkMode(TRANSPARENT);
      pDC->FillSolidRect(rect, ::GetSysColor(COLOR_BTNFACE));

      // Keep this order to prevent rollover effect occuring on active tab !
      if (bSelected) {
            pDC->SelectObject(m_TabFont);
            pDC->SetTextColor(BLUE);
            pDC->DrawText(label, rect, DT_SINGLELINE | DT_VCENTER | DT_CENTER);
      } else {
            pDC->SetTextColor(BLACK);
            pDC->DrawText(label, rect, DT_SINGLELINE | DT_VCENTER | DT_CENTER);
      }

      pDC->RestoreDC(nSavedDC);
}

The problem I am having is that when a tab becomes active, sometimes the text will be too big to fit on the tab, so it is not fully visible, due to being emboldened.  I cannot see how to make the width of the tab bigger to accomodate this.  I thought I could use GetTextExtent() somehow, but even so I don't know if this would work for emboldened text or how to adjust the tab's width manually.

How can I do this ?

TIA
Avatar of Jaime Olivares
Jaime Olivares
Flag of Peru image

Here is a dirty trick,
Name all tabs with a whitespace at the end of each label, this will leave enough space to all tabs when them are bolded, and will avoid you to resize tabs.
Avatar of mrwad99

ASKER

Hmm, I did

      m_wndMyTabCtrl.InsertItem(0, _T("This is tab one       "), 0);
      m_wndMyTabCtrl.InsertItem(1, _T("This is tab two       "), 0);
      m_wndMyTabCtrl.InsertItem(2, _T("This is tab three with a very long title    "), 0);

where m_wndMyTabCtr is the tab control, and this does not make any difference.  The space is included when the tab is highlighted, hence some of the text is still missing.  Is that not what you meant to do ?
> The space is included when the tab is highlighted, hence some of the text is still missing
My intention is to make whitespaces disappear. I see you don't specify a font for default tab, maybe both differ not only in bold style:

 // Keep this order to prevent rollover effect occuring on active tab !
     if (bSelected) {
          pDC->SelectObject(m_TabFont);
          pDC->SetTextColor(BLUE);
          pDC->DrawText(label, rect, DT_SINGLELINE | DT_VCENTER | DT_CENTER);    // This line could be outside if/else
     } else {
          // Missing font here, I suggest you to create a font for normal case too
          pDC->SetTextColor(BLACK);
          pDC->DrawText(label, rect, DT_SINGLELINE | DT_VCENTER | DT_CENTER);    // This line could be outside if/else
     }



Avatar of mrwad99

ASKER

Yeah I have seen those two links above, but am learning on the job so to speak so I want to know how to get around this problem myself.  I created another font for when the tab is not active but that makes no difference.  What I really want to be able to do is just adjust the size of the tab, or decrease the length of the text being displayed, maybe append "..." to it if necessary, eg

Really long tab t...

as opposed to

Really long tab title

How is the question.

?
Avatar of mrwad99

ASKER

Points doubled.
If you want to resize tabs by yourself use GetTextExtent to calculate text width, and use SetMinTabWidth() to resize:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/html/_mfc_CTabCtrl.3a3a.SetMinTabWidth.asp
Avatar of mrwad99

ASKER

Yeah that is fine, but SetMinTabWidth will adjust the size of *all* of the tabs.  

I have also been looking at some of the other tab control functions and it seems that all of them operate on *all* of the tabs; I just want to play with one at a time.  How can I call this function on just the concerned tab, i.e. the one that is getting the focus, hence the one that will have the emboldened text ?

Thanks for the help so far.
ASKER CERTIFIED SOLUTION
Avatar of Jaime Olivares
Jaime Olivares
Flag of Peru image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of AndyAinscow
Have a customised header control.  In that trap the HDN_ITEMCLICK (other possibles HDM_GETITEMRECT, HDN_ITEMCHANGING) message.  Find out what the new width of the tab is to be with the bold font.  Then use GetItem/SetItem to set the new width (mask = HDI_WIDTH, cxy = new width, of the HDITEM structure).  You should also reset the width of the previously active tab
Avatar of mrwad99

ASKER

Gentlemen.

Apologies for the delay.  I have in fact found the solution for this problem.  After reading http://www.codeguru.com/Cpp/controls/controls/tabcontrols/article.php/c2237/ 

"Since different weighted fonts will give different sized text, the larger of the fonts should be selected as the underlying font for the control so that the sizes of the tabs will be calculated correctly."

This is exactly what Jaime stated above; this link merely showed me how to do it.  Hence, I intend to close this question shortly giving Jaime the full points.

Andy:

you (as always) have raised an interesting issue there - a header control ?!  If you would care to discuss this further I will award you more points for your individual effort.

Thanks all :)
By all means award the points to Jaime if you adopt his suggestion.

It was a (possibly silly) idea of having a owner draw header control instead of the tabcontrol.  I haven't actually tried it but with the header one could  modify the widths on the fly when another section of the header is clicked.  You would respond to the click on the header as you would the click on a tab control.
Avatar of mrwad99

ASKER

Oh right, I see.

Yeah, I suppose that would not be the same as having the tab controls really.  Well, the suggestion was there, and as always it is appreciated.
Thanks, for the points, glad to be useful.
BR,
Jaime.
I had another thought  whilst walking the dog.

Leave the fonts alone.
For the active tab change the text dynamically and add say __ (just some characters that are not going to appear in the normal text).  The tab control will widen to cope with this.  When the active tab loses its active status then trim this text off and set back to the correct text.  This will narrow the tab to fit.
Now when you draw your text just trim these characters off.

This should result in each tab having a similar margin.  This patch with the font (I suspect) will have a number of tabs looking grotty with a large margin round the text compared to the active tab.
Avatar of mrwad99

ASKER

Andy,

I think what you are asking is similar to what Jaime suggested on 10/30/2004 07:35PM BST.  I did not notice any grottiness (?) on the tabs when using the selected method, but if you have any more suggestions then please post.

Cheers !
I noted your response that the extra space was not allocated when Jaime suggested whitespace, I thought maybe windows is being clever and trimming the text.  Note I didn't suggest whitespace and I suggested only assigning the extra chars when the tab was set as the selected one (dynamically modifying the text, but you only display the 'real' text).


Grottiness - I'll try to give you my mental picture of what you are describing.
treat the | as the vertical line when the tabs are drawn. Text on tabs - first, selected, last

Default font
| first |ELECTE| last |      <----- truncated text

set your large font for all
|    first     | SELECTED |      last      |   <-------  large margins around text in tabs with the default font

What I thought was that when the second tab is selected you dynamically change the text to eg selected___  The tab control will then resize to make it fit.  However when you draw the text you trim the rightmost ___ chars off and only display selected in you larger font BUT in a now oversized rectangle to result in
| first | SELECTED | last |    <---- large font but similar left/right margins around each text
Avatar of mrwad99

ASKER

Ah, I see what you are mentioning now AndyAinscow.  I must say that I have not experienced any of these problems as yet, but, if I do, I will try this suggestion and award points if the idea here works.

Thanks.