Delphi Xe4. Form, ActionManager, ImageList (with 32x32 Icons), ActionMainMenuBar.
I can not ensure that the icons are displayed correctly. What should you do?
At the same time, if I apply any vcl style of decoration, it displays fine. But if the style of "Windows" by default, the text moves out of the icon. Help.
Sorry for the bad English.
This is a valid question, the TActionMainMenuBar
is meant to be designed to be able to handle custom icon sizes as menu images, just as the native menus can handle them fine. One indication of that can be found in the comments in the code, f.i. in the below VCL code you can find the comment 16 is standard image size so adjust for larger images
.
The faulty code, I believe, is in TCustomMenuItem.CalcBounds
in 'ActnMenus.pas'. Below excerpt is from D2007. Notice the line below I commented with some exclamation marks. After the ascendant class TCustomActionControl
calculates the positioning of text and image in its CalcLayout
method, the TCustomMenuItem
ruins it with the hard-coded 24 in the said statement.
procedure TCustomMenuItem.CalcBounds;
var
AWidth, AHeight: Integer;
NewTextBounds: TRect;
ImageSize: TPoint;
ImageOffset: Integer;
begin
inherited CalcBounds;
ImageSize := GetImageSize;
AHeight := FCYMenu;
if Separator then
AHeight := FCYMenu div 3 * 2
else
// 16 is standard image size so adjust for larger images
if ImageSize.Y > 16 then
AHeight := ImageSize.Y + 4;
if ActionClient = nil then exit;
if ImageSize.X <= 16 then
ImageOffset := 24
else
ImageOffset := ImageSize.X + 6; // Leave room for an image frame
NewTextBounds := TextBounds;
OffsetRect(NewTextBounds, 24 - TextBounds.Left, // <- !!!!!
AHeight div 2 - TextBounds.Bottom div 2 - 1);
TextBounds := NewTextBounds;
ShortCutBounds := Rect(0,0,0,0);
if ActionClient.ShortCut <> 0 then
begin
Windows.DrawText(Canvas.Handle, PChar(ActionClient.ShortCutText), -1,
FShortCutBounds, DT_CALCRECT);
// Left offset is determined when the item is painted to make it right justified
FShortCutBounds.Top := TextBounds.Top;
FShortCutBounds.Bottom := TextBounds.Bottom;
AWidth := TextBounds.Right + FShortCutBounds.Right + ImageOffset + Spacing;
end
else
AWidth := TextBounds.Right + TextBounds.Left;
SetBounds(Left, Top, AWidth, AHeight);
end;
The 24 is an assumption based on images having 16 or less pixels width. What should be used instead is the ImageOffset
value calculated just a few lines above. Replace
OffsetRect(NewTextBounds, 24 - TextBounds.Left,
AHeight div 2 - TextBounds.Bottom div 2 - 1);
with
OffsetRect(NewTextBounds, ImageOffset - TextBounds.Left,
AHeight div 2 - TextBounds.Bottom div 2 - 1);
and you'll have something like this:
You'll notice some other weirdness though, items not having images are still settling for a small image layout. IMO all menu items should have the same basic layout, but the design of action menus allow different layouts for individual items. One other weird thing is the checked state of an item with an image ('Action6'), although I'm not sure if I'm missing a setting here or if it would qualify as a bug otherwise.