(Phoenix) Toolbars and how to switch Toolbar-Text at runtime on/off

Started by Theo Gottwald, July 01, 2007, 08:10:01 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Theo Gottwald

I have discussed this topic with Dominic Mitchell, and the following Text is a part from what he told me.
While the informations are not at all general but partly specific to my actual project, it shows some "How To's " with Toolbars that may be of interest to anyone having to do with toolbars.


Toolbar and Phoenix
There is a bug in the Windows toolbar that causes a stack fault when the toolbar has a certain
combination of styles and it is moved, or setting or clearing a style causes it to move.

The following of some of my notes that describe some of the bugs in the Windows toolbar.

Memory leak
Rapidly creating and destroying toolbars that use the system bitmaps in commctrl.dll
produces a nasty leak in GDI resources.  To make matters worse the resources are
not restored even after the application terminates.  Toolbars that do not use the
system bitmaps do not exhibit this problem.
I cannot think of a single common control supplied by Microsoft that does not
have a plethora of bugs.

CCS_NOPARENTALIGN and dynamic resizing
A toolbar with is style may enter a WM_SIZE loop that can lead to a stack fault
when it is dynamically resized.  Use the CCS_NORESIZE style for toolbars
that deeply embedded in for example rebars.  When a toolbar with the CCS_NOPARENTALIGN
style is resized, it walks across the screen.

Common controls with a divider(CCS_NODIVIDER style not set)
have a tendency to move vertically when their size or position
is changed.  To prevent this behaviour, remove the divider style
before setting size and position properties.

There is a check in the designer that attempts to break out of the runaway recursion, but even
that fails sometimes.

Your modified code

You have three options,
1) Use the original code I sent with a toolbar with the TBSTYLE_FLAT, TBSTYLE_LIST,
   TBSTYLE_EX_MIXEDBUTTONS and CCS_NORESIZE styles.  The drawback with this method is that when the
   buttons have the BTNS_SHOWTEXT style, the text is displayed to the right of the image.

2) Destroy and recreate the toolbar.  This involves the most work because the toolbar state
   will have to be stored somewhere in order for it to be rebuilt.  The toolbar state can be stored
   in the registry or a stream.

3) Vary the size of the toolbar buttons in such a way that the text is hidden or visible.  This allows
   text to be displayed at the bottom or right of the button image.

The rest of this document describes the third option.

Modify layout rules
Open the Layout Manager and delete the rule that produces the following macro:

//Action                  Act On                                  Relative To
//-------  ------------------------------------  ----------------------------------------------
// Macro    Part       Widget(First, Last)        Part            Widget             %   Offset
//-------  ------  ----------------------------  ------  -------------------------  ---  ------
  STRETCH, BOTTOM, IDC_MDIFORM1_PAGER1,       0, HEIGHT, IDD_MDIFORM1,               10,      0

Deleting the rule allows the pager to change its size as the height of the toolbar changes.

Toolbar control styles
Only the following styles should be set for the toolbar control:

Make sure TBSTYLE_EX_MIXEDBUTTONS is not checked.

Toolbar button styles
If the button is not a separator, it style should be BTNS_BUTTON, at the very least, it should

Code to vary size of buttons

FUNCTION Toolbar_ToggleText _
  ( _
  BYVAL hWndToolbar AS DWORD, _ ' handle of toolbar control
  BYVAL fShowText   AS LONG _   ' true = show text, false = hide text

  LOCAL szText            AS ASCIIZ * %MAX_PATH
  LOCAL trc               AS RECT
  LOCAL tsize             AS SIZEL
  LOCAL tbbi              AS TBBUTTONINFO
  LOCAL iButton           AS LONG
  LOCAL cButtons          AS LONG
  LOCAL hWndLabel         AS DWORD
  LOCAL iBtnWithLabel     AS LONG
  LOCAL x                 AS LONG
  LOCAL y                 AS LONG
  LOCAL cxCtrl            AS LONG
  LOCAL cyCtrl            AS LONG
  LOCAL cyOffset          AS LONG
  LOCAL cxyButton         AS LONG
  LOCAL hImgListNormal    AS DWORD
  LOCAL hFont             AS DWORD
  LOCAL cxIcon            AS LONG
  LOCAL cyIcon            AS LONG
  LOCAL hDC               AS DWORD
  LOCAL cxText            AS LONG
  LOCAL cyText            AS LONG
  LOCAL hWndPager         AS DWORD
  LOCAL cxPager           AS LONG
  LOCAL cyPager           AS LONG

  ' If text is being shown
  IF fShowText THEN
    ' Calcualte width and height of buttons
    hImgListNormal = SendMessage(hWndToolbar,  %TB_GETIMAGELIST, 0, 0)
    ImageList_GetIconSize hImgListNormal, BYVAL VARPTR(cxIcon), BYVAL VARPTR(cyIcon)

    hDC = GetDC(hWndToolbar)
    SaveDC hDC
    SelectObject hDC, SendMessage(hWndToolbar, %WM_GETFONT, 0, 0)

    tbbi.cbSize  = SIZEOF(tbbi)
    tbbi.pszText = VARPTR(szText)
    tbbi.cchText = %MAX_PATH

    cButtons = SendMessage(hWndToolbar, %TB_BUTTONCOUNT, 0, 0)
    iButton  = 0

    WHILE iButton < cButtons
      szText = ""
      IF SendMessage(hWndToolbar, %TB_GETBUTTONINFO, iButton, BYVAL VARPTR(tbbi)) THEN
        IF (tbbi.fsStyle AND %BTNS_SEP) <> %BTNS_SEP THEN
          SendMessage hWndToolbar, %TB_GETBUTTONTEXT, tbbi.idCommand, BYVAL VARPTR(szText)
          GetTextExtentPoint32 hDC, BYVAL tbbi.pszText, LEN(tbbi.@pszText), tsize
          cxText = MAX(cxText, tsize.cx)
          cyText = MAX(cyText, tsize.cy)
        END IF
      END IF
      INCR iButton

    RestoreDC hDC, -1

    SendMessage hWndToolbar, %TB_SETBUTTONSIZE, 0, MAKDWD(cxText + 6, cyIcon + cyText + 9)
  ' Text is being hidden
    ' Let the toolbar set the default size for the buttons
    SendMessage hWndToolbar, %TB_SETBUTTONSIZE, 0, MAKDWD(0, 0)

  hWndPager = GetParent(hWndToolbar)
  GetWindowRect hWndPager, trc
  cxPager = trc.nRight - trc.nLeft
  ' Height(6 pixels add to compensate for edges of the pager)
  cyPager = HIWRD(SendMessage(hWndToolbar, %TB_GETBUTTONSIZE, 0, 0)) + 6
  SetWindowPos hWndPager, %NULL, 0, 0, cxPager, cyPager, %SWP_NOZORDER OR %SWP_NOMOVE OR %SWP_DRAWFRAME
  SendMessage hWndPager, %PGM_RECALCSIZE, 0, 0

' -------------------------------------------------------
  ' Reposition embedded control(s)
  ' Note: this code assumes that is an embedded control at the
  '       location of button 17.
  hWndLabel = GetDlgItem(hWndToolbar, %IDC_MDIFORM1_TEXTLBL1)
  GetWindowRect hWndLabel, trc
  cxCtrl = trc.nRight - trc.nLeft
  cyCtrl = trc.nBottom - trc.nTop

  ' Get the bounding rectangle of the separator
  iBtnWithLabel = 17
  cyOffset = 1
  SendMessage hWndToolbar, %TB_GETITEMRECT, iBtnWithLabel, BYVAL VARPTR(trc)
  ' Calculate new position of the label
  x = (trc.nLeft + trc.nRight - cxCtrl) \ 2
  y = (trc.nTop + trc.nBottom - cyCtrl) \ 2 + cyOffset
  SetWindowPos hWndLabel, %NULL, x, y, cxCtrl, cyCtrl, %SWP_NOZORDER OR %SWP_DRAWFRAME
  ' -------------------------------------------------------

How to invoke code above

FUNCTION MDIForm1_MainMenu1_ViewToolbar _
  ( _
  BYVAL hWnd  AS DWORD _  ' handle of window that owns the menu

  LOCAL hWndPager     AS DWORD
  LOCAL hWndToolbar   AS DWORD
  LOCAL hMenu         AS DWORD
  LOCAL fChecked      AS LONG

  hMenu = GetMenu(hWnd)
  fChecked = ((GetMenuState(hMenu, %IDM_TOOLBAR, %MF_BYCOMMAND) AND %MF_CHECKED) = %MF_CHECKED)

  IF fChecked THEN

  hWndPager   = GetDlgItem(hWnd, %IDC_MDIFORM1_PAGER1)
  hWndToolbar = GetDlgItem(hWndPager, %IDC_MDIFORM1_TOOLBAR1)

  Toolbar_ToggleText hWndToolbar, NOT fChecked

  ' Update the layout
  SendMessage hWnd, gdwADM_LAYOUT, hWnd, %IDD_MDIFORM1