View Issue Details

IDProjectCategoryView StatusLast Update
0001675JEDI VCL00 JVCL Componentspublic2004-06-25 05:05
ReporterglchapmanAssigned Touser72 
PrioritynormalSeverityfeatureReproducibilityalways
Status resolvedResolutionfixed 
Product Version 
Target VersionFixed in Version 
Summary0001675: Add support for flat menubars to TJvXPMenuItemPainter
DescriptionStarting with Windows XP, menubars now have a different default background color if SPI_GETFLATMENU is true. The attached patch slightly modifies TJvXPMenuItemPainter so that it uses this background color when owner-drawing (if appropriate).
TagsNo tags attached.

Activities

2004-04-20 16:23

 

JvMenus.pas.diff (2,239 bytes)
*** JvMenus.pas.orig	Mon Mar 29 15:47:22 2004
--- JvMenus.pas	Tue Apr 20 16:27:58 2004
***************
*** 660,665 ****
--- 660,666 ----
      // other usage fields
      FSelRect: TRect;
      FCheckedPoint: TPoint;
+     FFlatMenubar: boolean;
  
      procedure DrawBitmapShadow(X, Y: integer; B: TBitmap);
      procedure DrawImageBackground(ARect: TRect); override;
***************
*** 2766,2771 ****
--- 2767,2773 ----
  end;
  
  procedure TJvXPMenuItemPainter.DrawItemBackground(ARect: TRect);
+ const COLOR_MENUBAR = 30;
  begin
    with Canvas do
    begin
***************
*** 2783,2797 ****
        begin
          Brush.Assign(SelectionFrameBrush);
          Pen.Assign(SelectionFramePen);
        end
        else
!       begin
!         Brush.Color := clBtnFace;
!         Brush.Style := bsSolid;
!         Pen.Style := psClear;
!       end;
!       Rectangle(ARect);
! 
      end;
    end;
  end;
--- 2785,2806 ----
        begin
          Brush.Assign(SelectionFrameBrush);
          Pen.Assign(SelectionFramePen);
+         Rectangle(ARect);
        end
        else
!         if FFlatMenubar then
!         begin
!           Brush.Color := GetSysColor(COLOR_MENUBAR);
!           Brush.Style := bsSolid;
!           FillRect(ARect);
!         end
!         else
!         begin
!           Brush.Color := clBtnFace;
!           Brush.Style := bsSolid;
!           Pen.Style := psClear;
!           Rectangle(ARect);
!         end;
      end;
    end;
  end;
***************
*** 3056,3061 ****
--- 3065,3077 ----
      );
  end;
  
+ function CheckFlatMenubar: boolean;
+ const SPI_GETFLATMENU = $1022;
+ var b: BOOL;
+ begin
+   Result:= SystemParametersInfo(SPI_GETFLATMENU, 0, @b, 0) and b;
+ end;
+ 
  constructor TJvXPMenuItemPainter.Create(AOwner: TComponent);
  begin
    inherited;
***************
*** 3075,3080 ****
--- 3091,3097 ----
    FSeparatorColor := DefaultXPSeparatorColor;
    FCheckedImageBackColor := DefaultXPCheckedImageBackColor;
    FCheckedImageBackColorSelected := DefaultXPCheckedImageBackColorSelected;
+   FFlatMenubar:= CheckFlatMenubar;
  end;
  
  procedure TJvXPMenuItemPainter.UpdateFieldsFromMenu;
JvMenus.pas.diff (2,239 bytes)

glchapman

2004-04-20 17:19

reporter   ~0003974

Here's a slightly different version of the patch (JvMenus.pas.diff.2) which includes a WM_SETTINGCHANGE handler.

2004-04-20 17:20

 

JvMenus.pas.diff.2 (2,111 bytes)

user72

2004-04-23 02:46

  ~0004029

I would prefer you post the entire unit (zipped, please) because I don't have a tool to merge or view diff files.

2004-04-23 05:27

 

JvMenus.pas (103,609 bytes)

glchapman

2004-04-23 05:35

reporter   ~0004030

As requested, I just attached my modified JvMenus.pas. However, I forgot to zip it. I apologize for botching that; I'd replace it with the zip file, but there doesn't seem to be any way for me to do that.

user72

2004-04-23 06:01

  ~0004032

No problem, I'll check it out

user72

2004-04-23 06:15

  ~0004033

Just curious: how does one set the flat menu style in XP?

I just tried on a friends XP and we couldn't see any difference even after changing every visual style we could find...

glchapman

2004-04-24 09:58

reporter   ~0004057

The one way I know of to change the flat menubar setting is to use SystemParametersInfo with SPI_SETFLATMENU:

SystemParametersInfo(SPI_SETFLATMENU, 0, pointer(value), 1);

where value is a BOOL (note the bizarre use of pvParam rather than uiParam for passing in the value -- contrary to what I first thought, passing in a pointer to a BOOL will always set Flat Menus to true (unless the pointer is nil), regardless of the value of the BOOL). If the setting was orignally true, changing it to false will cause normal menus (notepad's, for example) to paint the menu text with a background of clBtnFace. However the menubar itself still uses COLOR_MENUBAR. No WM_SETTINGCHANGE is issued.

So, apparently adding WM_SETTINGCHANGE handling was pointless. It appears that normal menus test the value of SPI_GETFLATMENU every time they paint text, though the menubar background is not changed until you do something like a theme change. This suggests changing my patch to either 1) check SystemParametersInfo every time the menubar is painted or 2) or look for a WM_THEMECHANGED message and update GFlatMenubar when it is received.

Option 2 seems to me to be best. Would you like me to attach a new patched JvMenus (zipped this time)?

By the way, there's apparently a bug in XP where if you switch from the Windows XP theme to a saved theme based on the Windows XP theme, SPI_GETFLATMENU returns false, but the menubar background continues to use COLOR_MENUBAR. Note that this breaks normal menus (like notepad's) as well as the TJvXPMenuItemPainter. I just spent way too much time looking for a workaround, but I haven't found anything satisfactory (I suspect a themed app may be able to use GetThemeSysBool(ThemeServices.Theme[teMenu], TMT_FLATMENUS) to stay in sync with XP, but I didn't try it.)

glchapman

2004-04-24 10:40

reporter   ~0004058

A couple of notes: the SPI_SETFLATMENU should have been:

SystemParametersInfo(SPI_SETFLATMENU, 0, pointer(value), SPIF_SENDCHANGE);

But it doesn't matter because Windows still doesn't send WM_SETTINGCHANGE. It looks like it instead invalidates all windows, which forces any menus to redraw.

Also, I note from here:

http://homepages.borland.com/strefethen/index.php?pagename=Main.Delphi7

that Borland has implemented some handling for SPI_GETFLATMENU in Delphi 7. Perhaps someone who has the source for this could peek at it and see what the right way to handle this is.

user72

2004-04-24 23:55

  ~0004063

> The one way I know of to change the flat menubar
> setting is to use SystemParametersInfo
But isn't there a way to do it in the Windows UI?

> Would you like me to attach a new patched JvMenus (zipped this time)?
Please do

glchapman

2004-04-26 11:18

reporter   ~0004087

First, I think the only way to change flat menus through the UI is to change the Desktop Theme.

Second, my suggestion of handling WM_THEMECHANGED won't work because the message is sometimes sent too early. In testing a switch from the Windows XP theme to the Classic theme, at the time WM_THEMECHANGED was received, SPI_GETFLATMENU returned true; once the theme change was complete, it returned false.

So I'm attaching a patch which tests for flat menus every time a top-level item is drawn. You only really need to test once for each WM_NCPAINT, but the test has to be done before the default handling of WM_NCPAINT (which is where the WM_DRAWITEMs are generated). TJvMainMenu uses an hoAfterMsg hook, so its current hook can't be used. I didn't add a new hook because, on my system anyway, there's no detectable slowdown from testing for each item.

By the way, all of this would be much easier if top-level items were not created with MFT_OWNERDRAW (does anyone ever use real owner-drawing (images, etc.) for the items in the menubar itself)? But I don't think there's a good way to get TMenuItem to do that.

Anyway, a couple of other things. The test for flat menubars first tests for IsWinXP_UP (I stole the code for this from JvBalloonHint.pas -- you might want to combine this and other similar tests scattered through JVCL into some sort of utility unit). This is because, according to a comment in the Toolbar2000 source, if the proper bit is set in the UserPreferencesMask in the registry, SPI_GETFLATMENU will succeed (and return true) on Windows 2000, even though flat menus are not supported there.

Both SPI_GETFLATMENU and COLOR_MENUBAR are likely defined in newer units in rtl\win for versions after Delphi 5; you might want to surround their definitions in JvMenus with IFDEFS so they are only defined there for versions which need them.

Finally, I made UseFlatMenubars a public function, because it seemed like someone might want to create their own ItemPainter class, and they would potentially need to check this.

2004-04-26 11:19

 

JvMenus.zip (18,855 bytes)

user72

2004-04-27 01:19

  ~0004092

I have no way of testing this (stil don't have XP) but I will commit it since that is the only way to get feedback (most users won't try code posted here).

user72

2004-06-25 05:05

  ~0004628

No reply, so assuming it works (already in CVS)

Issue History

Date Modified Username Field Change
2004-04-20 16:23 glchapman New Issue
2004-04-20 16:23 glchapman File Added: JvMenus.pas.diff
2004-04-20 17:19 glchapman Note Added: 0003974
2004-04-20 17:20 glchapman File Added: JvMenus.pas.diff.2
2004-04-23 02:46 user72 Note Added: 0004029
2004-04-23 05:27 anonymous File Added: JvMenus.pas
2004-04-23 05:35 glchapman Note Added: 0004030
2004-04-23 06:01 user72 Note Added: 0004032
2004-04-23 06:15 user72 Note Added: 0004033
2004-04-24 09:58 glchapman Note Added: 0004057
2004-04-24 10:40 glchapman Note Added: 0004058
2004-04-24 23:55 user72 Note Added: 0004063
2004-04-24 23:56 user72 Status new => feedback
2004-04-26 11:18 glchapman Note Added: 0004087
2004-04-26 11:19 glchapman File Added: JvMenus.zip
2004-04-27 01:19 user72 Note Added: 0004092
2004-04-27 01:38 user72 Status feedback => assigned
2004-04-27 01:38 user72 Assigned To => user72
2004-06-25 05:05 user72 Status assigned => resolved
2004-06-25 05:05 user72 Resolution open => fixed
2004-06-25 05:05 user72 Note Added: 0004628