mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-12-15 11:30:29 +01:00
lcl: add ownerdrawn menu support (for now win32 only). Issue #26969
git-svn-id: trunk@51466 -
This commit is contained in:
parent
1aef9cf770
commit
62ef2702b3
@ -53,7 +53,7 @@ begin
|
|||||||
FItems.UpdateImages;
|
FItems.UpdateImages;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TMenu.SetBidiMode(const AValue: TBidiMode);
|
procedure TMenu.SetBiDiMode(const AValue: TBiDiMode);
|
||||||
begin
|
begin
|
||||||
if FBidiMode=AValue then exit;
|
if FBidiMode=AValue then exit;
|
||||||
FBidiMode:=AValue;
|
FBidiMode:=AValue;
|
||||||
@ -62,7 +62,7 @@ begin
|
|||||||
BidiModeChanged;
|
BidiModeChanged;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TMenu.SetParentBidiMode(const AValue: Boolean);
|
procedure TMenu.SetParentBiDiMode(const AValue: Boolean);
|
||||||
begin
|
begin
|
||||||
if FParentBiDiMode = AValue then exit;
|
if FParentBiDiMode = AValue then exit;
|
||||||
FParentBiDiMode := AValue;
|
FParentBiDiMode := AValue;
|
||||||
@ -275,7 +275,7 @@ end;
|
|||||||
|
|
||||||
The handle will be created if not already allocated.
|
The handle will be created if not already allocated.
|
||||||
------------------------------------------------------------------------------}
|
------------------------------------------------------------------------------}
|
||||||
function TMenu.GetHandle: HMenu;
|
function TMenu.GetHandle: HMENU;
|
||||||
begin
|
begin
|
||||||
HandleNeeded();
|
HandleNeeded();
|
||||||
Result := FItems.Handle;
|
Result := FItems.Handle;
|
||||||
@ -358,7 +358,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TMenu.IsBiDiModeStored: boolean;
|
function TMenu.IsBiDiModeStored: Boolean;
|
||||||
begin
|
begin
|
||||||
Result := not FParentBidiMode;
|
Result := not FParentBidiMode;
|
||||||
end;
|
end;
|
||||||
|
|||||||
@ -28,7 +28,7 @@ uses
|
|||||||
// To get as little as posible circles,
|
// To get as little as posible circles,
|
||||||
// uncomment only when needed for registration
|
// uncomment only when needed for registration
|
||||||
////////////////////////////////////////////////////
|
////////////////////////////////////////////////////
|
||||||
Graphics, GraphType, ImgList, Menus, Forms,
|
LCLType, Graphics, GraphType, ImgList, Menus, Forms,
|
||||||
////////////////////////////////////////////////////
|
////////////////////////////////////////////////////
|
||||||
WSMenus, WSLCLClasses, WSProc,
|
WSMenus, WSLCLClasses, WSProc,
|
||||||
Windows, Controls, Classes, SysUtils, Win32Int, Win32Proc, Win32WSImgList,
|
Windows, Controls, Classes, SysUtils, Win32Int, Win32Proc, Win32WSImgList,
|
||||||
@ -79,6 +79,7 @@ type
|
|||||||
function FindMenuItemAccelerator(const ACharCode: word; const AMenuHandle: HMENU): integer;
|
function FindMenuItemAccelerator(const ACharCode: word; const AMenuHandle: HMENU): integer;
|
||||||
procedure DrawMenuItemIcon(const AMenuItem: TMenuItem; const AHDC: HDC;
|
procedure DrawMenuItemIcon(const AMenuItem: TMenuItem; const AHDC: HDC;
|
||||||
const ImageRect: TRect; const ASelected: Boolean);
|
const ImageRect: TRect; const ASelected: Boolean);
|
||||||
|
function ItemStateToDrawState(const ItemState: UINT): LCLType.TOwnerDrawState;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
@ -486,6 +487,58 @@ begin
|
|||||||
Result.cx := Result.cx + Metrics.TextMargins.cxLeftWidth + Metrics.TextMargins.cxRightWidth;
|
Result.cx := Result.cx + Metrics.TextMargins.cxLeftWidth + Metrics.TextMargins.cxRightWidth;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function ClassicMenuItemSize(AMenuItem: TMenuItem; ADC: HDC): TSize;
|
||||||
|
var
|
||||||
|
LeftSpace, RightSpace: Integer;
|
||||||
|
oldFont: HFONT;
|
||||||
|
newFont: HFONT;
|
||||||
|
AvgCharSize: TSize;
|
||||||
|
begin
|
||||||
|
if AMenuItem.Default then
|
||||||
|
newFont := GetMenuItemFont([cfBold])
|
||||||
|
else
|
||||||
|
newFont := GetMenuItemFont([]);
|
||||||
|
oldFont := SelectObject(ADC, newFont);
|
||||||
|
AvgCharSize := GetAverageCharSize(ADC);
|
||||||
|
|
||||||
|
Result := StringSize(CompleteMenuItemCaption(AMenuItem, EmptyStr), ADC);
|
||||||
|
|
||||||
|
// Space between text and shortcut.
|
||||||
|
if AMenuItem.ShortCut <> scNone then
|
||||||
|
inc(Result.cx, AvgCharSize.cx);
|
||||||
|
|
||||||
|
GetNonTextSpace(AMenuItem, AvgCharSize.cx, LeftSpace, RightSpace);
|
||||||
|
inc(Result.cx, LeftSpace + RightSpace);
|
||||||
|
|
||||||
|
// Windows adds additional space to value returned from WM_MEASUREITEM
|
||||||
|
// for owner drawn menus. This is to negate that.
|
||||||
|
Dec(Result.cx, AvgCharSize.cx * 2);
|
||||||
|
|
||||||
|
// As for height of items in menu bar, regardless of what is set here,
|
||||||
|
// Windows seems to always use SM_CYMENUSIZE (space for a border is included).
|
||||||
|
|
||||||
|
if AMenuItem.IsLine then
|
||||||
|
Result.cy := GetSystemMetrics(SM_CYMENUSIZE) div 2 // it is a separator
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
if AMenuItem.IsInMenuBar then
|
||||||
|
begin
|
||||||
|
Result.cy := Max(Result.cy, GetSystemMetrics(SM_CYMENUSIZE));
|
||||||
|
if AMenuItem.hasIcon then
|
||||||
|
Result.cy := Max(Result.cy, aMenuItem.GetIconSize.y);
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
Result.cy := Max(Result.cy + 2, AvgCharSize.cy + 4);
|
||||||
|
if AMenuItem.hasIcon then
|
||||||
|
Result.cy := Max(Result.cy, aMenuItem.GetIconSize.y + 2);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
SelectObject(ADC, oldFont);
|
||||||
|
DeleteObject(newFont);
|
||||||
|
end;
|
||||||
|
|
||||||
procedure ThemeDrawElement(DC: HDC; Details: TThemedElementDetails; const R: TRect; ClipRect: PRect); inline;
|
procedure ThemeDrawElement(DC: HDC; Details: TThemedElementDetails; const R: TRect; ClipRect: PRect); inline;
|
||||||
begin
|
begin
|
||||||
with Details do
|
with Details do
|
||||||
@ -785,63 +838,30 @@ end;
|
|||||||
|
|
||||||
function MenuItemSize(AMenuItem: TMenuItem; AHDC: HDC): TSize;
|
function MenuItemSize(AMenuItem: TMenuItem; AHDC: HDC): TSize;
|
||||||
var
|
var
|
||||||
LeftSpace, RightSpace: Integer;
|
CC: TControlCanvas;
|
||||||
oldFont: HFONT;
|
ParentMenu: TMenu;
|
||||||
newFont: HFONT;
|
|
||||||
AvgCharSize: TSize;
|
|
||||||
begin
|
begin
|
||||||
|
ParentMenu := AMenuItem.GetParentMenu;
|
||||||
|
if (ParentMenu<>nil) and ParentMenu.OwnerDraw and Assigned(AMenuItem.OnMeasureItem) then
|
||||||
|
begin
|
||||||
|
CC := TControlCanvas.Create;
|
||||||
|
try
|
||||||
|
CC.Handle := AHDC;
|
||||||
|
AMenuItem.OnMeasureItem(AMenuItem, CC, Result.cx, Result.cy);
|
||||||
|
finally
|
||||||
|
CC.Free;
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
else
|
||||||
if IsVistaMenu then
|
if IsVistaMenu then
|
||||||
begin
|
begin
|
||||||
if AMenuItem.IsInMenuBar then
|
if AMenuItem.IsInMenuBar then
|
||||||
Result := VistaBarMenuItemSize(AMenuItem, AHDC)
|
Result := VistaBarMenuItemSize(AMenuItem, AHDC)
|
||||||
else
|
else
|
||||||
Result := VistaPopupMenuItemSize(AMenuItem, AHDC);
|
Result := VistaPopupMenuItemSize(AMenuItem, AHDC);
|
||||||
Exit;
|
|
||||||
end;
|
|
||||||
|
|
||||||
if AMenuItem.Default then
|
|
||||||
newFont := GetMenuItemFont([cfBold])
|
|
||||||
else
|
|
||||||
newFont := GetMenuItemFont([]);
|
|
||||||
oldFont := SelectObject(aHDC, newFont);
|
|
||||||
AvgCharSize := GetAverageCharSize(AHDC);
|
|
||||||
|
|
||||||
Result := StringSize(CompleteMenuItemCaption(AMenuItem, EmptyStr), AHDC);
|
|
||||||
|
|
||||||
// Space between text and shortcut.
|
|
||||||
if AMenuItem.ShortCut <> scNone then
|
|
||||||
inc(Result.cx, AvgCharSize.cx);
|
|
||||||
|
|
||||||
GetNonTextSpace(AMenuItem, AvgCharSize.cx, LeftSpace, RightSpace);
|
|
||||||
inc(Result.cx, LeftSpace + RightSpace);
|
|
||||||
|
|
||||||
// Windows adds additional space to value returned from WM_MEASUREITEM
|
|
||||||
// for owner drawn menus. This is to negate that.
|
|
||||||
Dec(Result.cx, AvgCharSize.cx * 2);
|
|
||||||
|
|
||||||
// As for height of items in menu bar, regardless of what is set here,
|
|
||||||
// Windows seems to always use SM_CYMENUSIZE (space for a border is included).
|
|
||||||
|
|
||||||
if AMenuItem.IsLine then
|
|
||||||
Result.cy := GetSystemMetrics(SM_CYMENUSIZE) div 2 // it is a separator
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
if AMenuItem.IsInMenuBar then
|
|
||||||
begin
|
|
||||||
Result.cy := Max(Result.cy, GetSystemMetrics(SM_CYMENUSIZE));
|
|
||||||
if AMenuItem.hasIcon then
|
|
||||||
Result.cy := Max(Result.cy, aMenuItem.GetIconSize.y);
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
begin
|
ClassicMenuItemSize(AMenuItem, AHDC);
|
||||||
Result.cy := Max(Result.cy + 2, AvgCharSize.cy + 4);
|
|
||||||
if AMenuItem.hasIcon then
|
|
||||||
Result.cy := Max(Result.cy, aMenuItem.GetIconSize.y + 2);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
SelectObject(aHDC, oldFont);
|
|
||||||
DeleteObject(newFont);
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function IsFlatMenus: Boolean; inline;
|
function IsFlatMenus: Boolean; inline;
|
||||||
@ -1084,6 +1104,33 @@ begin
|
|||||||
AImageList.Free;
|
AImageList.Free;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function ItemStateToDrawState(const ItemState: UINT): LCLType.TOwnerDrawState;
|
||||||
|
begin
|
||||||
|
Result := [];
|
||||||
|
if ItemState and ODS_SELECTED <> 0 then
|
||||||
|
Include(Result, LCLType.odSelected);
|
||||||
|
if ItemState and ODS_GRAYED <> 0 then
|
||||||
|
Include(Result, LCLType.odGrayed);
|
||||||
|
if ItemState and ODS_DISABLED <> 0 then
|
||||||
|
Include(Result, LCLType.odDisabled);
|
||||||
|
if ItemState and ODS_CHECKED <> 0 then
|
||||||
|
Include(Result, LCLType.odChecked);
|
||||||
|
if ItemState and ODS_FOCUS <> 0 then
|
||||||
|
Include(Result, LCLType.odFocused);
|
||||||
|
if ItemState and ODS_DEFAULT <> 0 then
|
||||||
|
Include(Result, LCLType.odDefault);
|
||||||
|
if ItemState and ODS_HOTLIGHT <> 0 then
|
||||||
|
Include(Result, LCLType.odHotLight);
|
||||||
|
if ItemState and ODS_INACTIVE <> 0 then
|
||||||
|
Include(Result, LCLType.odInactive);
|
||||||
|
if ItemState and ODS_NOACCEL <> 0 then
|
||||||
|
Include(Result, LCLType.odNoAccel);
|
||||||
|
if ItemState and ODS_NOFOCUSRECT <> 0 then
|
||||||
|
Include(Result, LCLType.odNoFocusRect);
|
||||||
|
if ItemState and ODS_COMBOBOXEDIT <> 0 then
|
||||||
|
Include(Result, LCLType.odComboBoxEdit);
|
||||||
|
end;
|
||||||
|
|
||||||
procedure DrawClassicMenuItemIcon(const AMenuItem: TMenuItem; const AHDC: HDC;
|
procedure DrawClassicMenuItemIcon(const AMenuItem: TMenuItem; const AHDC: HDC;
|
||||||
const ARect: TRect; const ASelected, AChecked: boolean);
|
const ARect: TRect; const ASelected, AChecked: boolean);
|
||||||
var
|
var
|
||||||
@ -1161,6 +1208,8 @@ procedure DrawMenuItem(const AMenuItem: TMenuItem; const AHDC: HDC; const ARect:
|
|||||||
var
|
var
|
||||||
ASelected, ANoAccel: Boolean;
|
ASelected, ANoAccel: Boolean;
|
||||||
B: Bool;
|
B: Bool;
|
||||||
|
ParentMenu: TMenu;
|
||||||
|
CC: TControlCanvas;
|
||||||
begin
|
begin
|
||||||
ASelected := (ItemState and ODS_SELECTED) <> 0;
|
ASelected := (ItemState and ODS_SELECTED) <> 0;
|
||||||
ANoAccel := (ItemState and ODS_NOACCEL) <> 0;
|
ANoAccel := (ItemState and ODS_NOACCEL) <> 0;
|
||||||
@ -1170,6 +1219,23 @@ begin
|
|||||||
else
|
else
|
||||||
else
|
else
|
||||||
ANoAccel := False;
|
ANoAccel := False;
|
||||||
|
|
||||||
|
ParentMenu := AMenuItem.GetParentMenu;
|
||||||
|
if (ParentMenu<>nil) and ParentMenu.OwnerDraw
|
||||||
|
and (Assigned(AMenuItem.OnDrawItem) or Assigned(AMenuItem.OnAdvancedDrawItem)) then
|
||||||
|
begin
|
||||||
|
CC := TControlCanvas.Create;
|
||||||
|
try
|
||||||
|
CC.Handle := AHDC;
|
||||||
|
if Assigned(AMenuItem.OnDrawItem) then
|
||||||
|
AMenuItem.OnDrawItem(AMenuItem, CC, ARect, ASelected);
|
||||||
|
if Assigned(AMenuItem.OnAdvancedDrawItem) then
|
||||||
|
AMenuItem.OnAdvancedDrawItem(AMenuItem, CC, ARect, ItemStateToDrawState(ItemState));
|
||||||
|
finally
|
||||||
|
CC.Free;
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
else
|
||||||
if IsVistaMenu then
|
if IsVistaMenu then
|
||||||
begin
|
begin
|
||||||
if AMenuItem.IsInMenuBar then
|
if AMenuItem.IsInMenuBar then
|
||||||
|
|||||||
18
lcl/menus.pp
18
lcl/menus.pp
@ -108,6 +108,13 @@ type
|
|||||||
mihtDestroy
|
mihtDestroy
|
||||||
);
|
);
|
||||||
|
|
||||||
|
TMenuDrawItemEvent = procedure(Sender: TObject; ACanvas: TCanvas;
|
||||||
|
ARect: TRect; ASelected: Boolean) of object;
|
||||||
|
TAdvancedMenuDrawItemEvent = procedure(Sender: TObject; ACanvas: TCanvas;
|
||||||
|
ARect: TRect; AState: TOwnerDrawState) of object;
|
||||||
|
TMenuMeasureItemEvent = procedure(Sender: TObject; ACanvas: TCanvas;
|
||||||
|
var AWidth, AHeight: Integer) of object;
|
||||||
|
|
||||||
TMenuItem = class(TLCLComponent)
|
TMenuItem = class(TLCLComponent)
|
||||||
private
|
private
|
||||||
FActionLink: TMenuActionLink;
|
FActionLink: TMenuActionLink;
|
||||||
@ -121,8 +128,11 @@ type
|
|||||||
FImageIndex: TImageIndex;
|
FImageIndex: TImageIndex;
|
||||||
FItems: TList; // list of TMenuItem
|
FItems: TList; // list of TMenuItem
|
||||||
FMenu: TMenu;
|
FMenu: TMenu;
|
||||||
|
FOnAdvancedDrawItem: TAdvancedMenuDrawItemEvent;
|
||||||
FOnChange: TMenuChangeEvent;
|
FOnChange: TMenuChangeEvent;
|
||||||
FOnClick: TNotifyEvent;
|
FOnClick: TNotifyEvent;
|
||||||
|
FOnDrawItem: TMenuDrawItemEvent;
|
||||||
|
FOnMeasureItem: TMenuMeasureItemEvent;
|
||||||
FParent: TMenuItem;
|
FParent: TMenuItem;
|
||||||
FMenuItemHandlers: array[TMenuItemHandlerType] of TMethodList;
|
FMenuItemHandlers: array[TMenuItemHandlerType] of TMethodList;
|
||||||
FSubMenuImages: TCustomImageList;
|
FSubMenuImages: TCustomImageList;
|
||||||
@ -283,6 +293,9 @@ type
|
|||||||
property Visible: Boolean read FVisible write SetVisible
|
property Visible: Boolean read FVisible write SetVisible
|
||||||
stored IsVisibleStored default True;
|
stored IsVisibleStored default True;
|
||||||
property OnClick: TNotifyEvent read FOnClick write FOnClick;
|
property OnClick: TNotifyEvent read FOnClick write FOnClick;
|
||||||
|
property OnDrawItem: TMenuDrawItemEvent read FOnDrawItem write FOnDrawItem;
|
||||||
|
property OnAdvancedDrawItem: TAdvancedMenuDrawItemEvent read FOnAdvancedDrawItem write FOnAdvancedDrawItem;
|
||||||
|
property OnMeasureItem: TMenuMeasureItemEvent read FOnMeasureItem write FOnMeasureItem;
|
||||||
end;
|
end;
|
||||||
TMenuItemClass = class of TMenuItem;
|
TMenuItemClass = class of TMenuItem;
|
||||||
|
|
||||||
@ -297,7 +310,11 @@ type
|
|||||||
FImageChangeLink: TChangeLink;
|
FImageChangeLink: TChangeLink;
|
||||||
FImages: TCustomImageList;
|
FImages: TCustomImageList;
|
||||||
FItems: TMenuItem;
|
FItems: TMenuItem;
|
||||||
|
FOnAdvancedDrawItem: TAdvancedMenuDrawItemEvent;
|
||||||
FOnChange: TMenuChangeEvent;
|
FOnChange: TMenuChangeEvent;
|
||||||
|
FOnDrawItem: TMenuDrawItemEvent;
|
||||||
|
FOnMeasureItem: TMenuMeasureItemEvent;
|
||||||
|
FOwnerDraw: Boolean;
|
||||||
FParent: TComponent;
|
FParent: TComponent;
|
||||||
FParentBiDiMode: Boolean;
|
FParentBiDiMode: Boolean;
|
||||||
FShortcutHandled: boolean;
|
FShortcutHandled: boolean;
|
||||||
@ -350,6 +367,7 @@ type
|
|||||||
property ParentBidiMode:Boolean read FParentBidiMode write SetParentBidiMode default True;
|
property ParentBidiMode:Boolean read FParentBidiMode write SetParentBidiMode default True;
|
||||||
property Items: TMenuItem read FItems;
|
property Items: TMenuItem read FItems;
|
||||||
property Images: TCustomImageList read FImages write SetImages;
|
property Images: TCustomImageList read FImages write SetImages;
|
||||||
|
property OwnerDraw: Boolean read FOwnerDraw write FOwnerDraw;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user