Can Delphi themed toolbars have dividers that are centred between their tool buttons?

后端 未结 3 1987
暗喜
暗喜 2020-12-11 17:27

I have noticed a rather annoying oddity with Delphi toolbars. I have a TToolbar that has logical groups of icons. To make the grouping stand out I would like to

相关标签:
3条回答
  • 2020-12-11 17:47

    This looks like a flaw in the VCL to me. The tbsDivider style does not map to an equivalent style in Win32. A tool button with that style is implemented in the VCL in just the same way as a tbsSeparator style, but with a custom paint method. This is extracted from TToolButton.Paint:

    if Style = tbsDivider then
      with Canvas do
      begin
        R := Rect(Width div 2 - 1, 0, Width, Height);
        if StyleServices.Enabled then
        begin
          Details := StyleServices.GetElementDetails(ttbSeparatorNormal);
          StyleServices.DrawElement(Handle, Details, R);
        end
        else
          DrawEdge(Handle, R, EDGE_ETCHED, BF_LEFT)
      end;
    

    In the old days of pre-v6 comctl32, the tbsSeparator style maps to a Win32 TBSTYLE_SEP style tool button. And in pre-v6 comctl32 that renders simply as a space with no vertical lines. The VCL designers clearly wanted to do more and added tbsDivider with the custom painting above.

    Fast forward to v6 comctl32. Now the common control draws a vertical line at the left hand edge of all TBSTYLE_SEP separators. So the code above simply adds an extra vertical line in the middle of the separator.

    We can try to get rid of the left-hand vertical line from a tbsDivider by modifying the code like this:

    if Style = tbsDivider then
      with Canvas do
      begin
        if StyleServices.Enabled then
        begin
          //re-paint the background to remove the vertical line drawn
          //for the standard separator button
          R := Rect(0, 0, Width, Height);
          StyleServices.DrawParentBackground(FToolBar.Handle, Handle, nil, False, R);
        end;
    
        R := Rect(Width div 2 - 1, 0, Width, Height);
        if StyleServices.Enabled then
        begin
          Details := StyleServices.GetElementDetails(ttbSeparatorNormal);
          StyleServices.DrawElement(Handle, Details, R);
        end
        else
          DrawEdge(Handle, R, EDGE_ETCHED, BF_LEFT)
      end;
    

    However, that doesn't work out because there's a lot of flickering as the left hand line is drawn and then painted over.

    I suspect that the VCL designers simply missed this arcane detail in the transition to v6 comctl32.

    I'll submit a QC report in due course.

    0 讨论(0)
  • 2020-12-11 17:51

    The native control draws the vertical line for a separator button when the toolbar has the flat style. So if you remove the flat style, you'd be left alone with the VCL's divider line. You can safely remove the style when the application is themed, themed toolbar buttons does not regard flat style (why toolbar separators does, I have no idea). However when themes are disabled there'll again be two lines. In that case keeping separators instead of dividers seems like the better option.

    One would guess unsetting the Flat property would have any effect as the documentation states. However TToolBar.CreateParams unconditionally enables it when StyleServices is enabled. So an API call is necessary;

    procedure TForm1.FormCreate(Sender: TObject);
    var
      TbStyle: DWORD;
    begin
      if StyleServices.Enabled then begin
        TbStyle := SendMessage(ToolBar1.Handle, TB_GETSTYLE, 0, 0);
        SendMessage(Toolbar1.Handle, TB_SETSTYLE, 0, TbStyle and not TBSTYLE_FLAT);
      end;
    end;
    


    This eliminates part of the problem, the remaining part is the divider line is not exactly in the center between two buttons. VCL's problem here is, it does not want to draw the line itself. So it calls the theme api which draws the separator line to the left of the separator. To circumvent, VCL passes about the right half of the separator rectangle to the api, and the line gets about in the middle. I don't know if there's any way to tell exactly where the theme api draws it and I doubt there is.

    0 讨论(0)
  • 2020-12-11 17:52

    As already explained, you can't do this using TToolBar due to changes in the Operating System.

    However, by using ActionToolBars you can achieve what you want to achieve - even with the current version of Delphi (XE6) and Windows 8.1/2012 R2 theming. Here's the steps:

    1. Create an Action Manager
    2. Add as many actions as you intend to have
    3. Add an ImageList (and another optional one for Disabled Menu Items)
    4. Associate each one of your actions with an image
    5. Add an ActionToolBar to the form
    6. Drag and drop each action you wish to appear on the toolbar to the toolbar
    7. Set individual actions on the toolbar to ShowCaption := False (if desired)
    8. Drag and drop the "Drag to create Separators" (from the ActionManager editor) box to add separators
    9. Drag and drop the ActionClientItems (created when you drag and drop actions onto the ActionTooLBar) to where you'd like them to be.

    End Result - A nice looking app with dividers betwen your icons.

    enter image description here

    0 讨论(0)
提交回复
热议问题