Shareware Beach

Thursday, 22 November 2007

TMainMenu and Screen Readers

Filed under: Software Development — Jan @ 17:31

I figured out why screen readers don’t read the menu bar in EditPad Pro 6, even though it’s a plain vanilla Delphi TMainMenu. Since the menu uses images, it is an “owner-drawn” menu. That means that the Delphi code in Menus.pas does the drawing, rather than Windows. Screen readers apparently don’t like that.

There are several ways to fix this in your application. A simple fix is to set TMainMenu.Images and TPopupMenu.Images to nil as a user preference.

A more elegant fix, requiring no user intervention, is to call SystemParametersInfo(SPI_GETSCREENREADER, 0, @ScreenReaderActive, 0); where ScreenReaderActive is a variable of type BOOL. If the call sets it to True, the user has a screen reader, and menu Images should be set to nil.

A permanent fix is to edit Menus.pas to never owner-draw anything. Then you can’t forget removing the images from any menu. Define var ScreenReaderActive: BOOL; near the top of the unit’s implementation section. Put the SystemParametersInfo(SPI_GETSCREENREADER, 0, @ScreenReaderActive, 0); call in the initialization section. That handles the detection.

Then find the TMenuItem.AppendTo procedure. There’s a line:

IsOwnerDraw := Assigned(ParentMenu) and

Change it to:

IsOwnerDraw := not ScreenReaderActive and Assigned(ParentMenu) and

In TMenuItem.AdvancedDrawItem, below the (long) nested procedures, there is a line:

if (ParentMenu <> nil) and (ParentMenu.OwnerDraw or (ImageList <> nil)) and

Append not ScreenReaderActive and to this line.

Finally, change the TMenu.IsOwnerDraw function to:

Result := not ScreenReaderActive and (OwnerDraw or (Images <> nil));

Now your menu items will never be owner drawn when there’s a screen reader active.

P.S.: For Delphi to actually use a unit you’ve edited, you’ll need to delete its .dcu files from the \lib and \lib\debug folders in your Delphi install folder, and copy or move the modified .pas file to a folder that’s on Delphi’s library path. You can edit the implementation section of any unit with impunity. Editing the implementation section will cause the compiler to refuse any units that are linked from .dcu files rather than compiled from a .pas file that have the modified unit in their uses clause.

1 Comment

  1. Do note that some products which are not screen readers in the traditional sense (like Dragon Naturally Speaking) use the screen reader interface. So you may get a “false positive” here.

    Comment by Craig Stuntz — Monday, 26 November 2007 @ 21:31

Sorry, the comment form is closed at this time.