Cocoa: macOS_AppMenuIntf added to facilitate APP to operate App Menu

This commit is contained in:
rich2014 2024-05-27 22:08:19 +08:00
parent 3635aa3044
commit 4df4532e13
3 changed files with 162 additions and 36 deletions

View File

@ -770,20 +770,19 @@ var
lCurItem: TMenuItem;
lMenuObj: NSObject;
lNSMenu: NSMenu absolute AMenu;
appleMenuFound: Boolean = false;
begin
if Assigned(PrevMenu) then PrevMenu.release;
PrevMenu := NSApplication(NSApp).mainMenu;
PrevMenu.retain;
PrevLCLMenu := CurLCLMenu;
if (lNSMenu.isKindOfClass(TCocoaMenu)) then
TCocoaMenu(lNSMenu).attachAppleMenu();
NSApp.setMainMenu(lNSMenu);
CurLCLMenu := ALCLMenu;
if (ALCLMenu = nil) or not ALCLMenu.HandleAllocated then Exit;
if (ALCLMenu = nil) or not ALCLMenu.HandleAllocated then begin
NSApp.setMainMenu(lNSMenu);
Exit;
end;
// Find the Apple menu, if the user provided any by setting the Caption to
// Some older docs say we should use setAppleMenu to obtain the Services/Hide/Quit items,
@ -800,9 +799,18 @@ begin
if TCocoaMenuItem(lMenuObj).isValidAppleMenu() then
begin
TCocoaMenu(lNSMenu).overrideAppleMenu(TCocoaMenuItem(lMenuObj));
appleMenuFound:= true;
Break;
end;
end;
if lNSMenu.isKindOfClass(TCocoaMenu) then begin
if NOT appleMenuFound then
TCocoaMenu(lNSMenu).createAppleMenu();
TCocoaMenu(lNSMenu).attachAppleMenu();
end;
NSApp.setMainMenu(lNSMenu);
end;
procedure TCocoaWidgetSet.SetMainMenu(const AMenu: HMENU; const ALCLMenu: TMenu);

View File

@ -48,6 +48,7 @@ type
FMenuItemTarget: TMenuItem;
procedure UncheckSiblings(AIsChangingToChecked: LCLObjCBoolean = False); message 'UncheckSiblings:';
function GetMenuItemHandle(): TMenuItem; message 'GetMenuItemHandle';
function FindSubmenuByLclItem(lclItem: TMenuItem): TCocoaMenuItem; message 'FindSubmenuByLclItem:';
procedure lclItemSelected(sender: id); message 'lclItemSelected:';
function lclGetCallback: IMenuItemCallback; override;
procedure lclClearCallback; override;
@ -62,9 +63,30 @@ type
function worksWhenModal: LCLObjCBoolean; message 'worksWhenModal';
end;
{ TMacOS_AppMenuIntf }
// Application interface provided to facilitate APP to operate App Menu.
// it's easy to set About, Preferences, and customized menus,
// only the LCL TMenuItem is needed to pass in.
// and we can control whether Cocoa is needed to automatically add
// Hide, Hide Others, Show All, and Quit menu items.
TMacOS_AppMenuIntf = class
public
aboutItem: TMenuItem;
preferencesItem: TMenuItem;
customMenus: TMenuItem;
dontAutoCreateAppMenuItems: Boolean;
end;
TMenuItemHandleCreateFunc = function(const AMenuItem: TMenuItem): NSMenuItem;
const
isMenuEnabled : Boolean = true;
var
macOS_AppMenuIntf: TMacOS_AppMenuIntf;
menuItemHandleCreateFunc: TMenuItemHandleCreateFunc;
procedure MenuTrackStarted(mn: NSMenu);
procedure MenuTrackEnded(mn: NSMenu);
procedure MenuTrackCancelAll;
@ -79,6 +101,7 @@ function ToggleAppMenu(ALogicalEnabled: Boolean): Boolean;
procedure Do_SetCheck(const ANSMenuItem: NSMenuItem; const Checked: boolean);
function FindEditMenu(const menu:NSMenu; const title:NSString): NSMenuItem;
procedure NSMenuAddItemsFromLCLMenu(menu: NSMenu; lclMenu: TMenuItem);
implementation
@ -221,22 +244,6 @@ begin
end;
end;
function FindEditMenu(const menu: NSMenu; const title: NSString): NSMenuItem;
var
index: NSInteger;
begin
if NOT Assigned(menu) then
Exit;
index:= menu.indexOfItemWithTitle(title);
if index >= 0 then begin
Result:= menu.itemAtIndex(index);
Exit;
end;
Result:= FindEditMenuByKeyEquivalent(menu, NSSTR('c')); // Command+C
end;
function getHotkeyFromTitle( aTitle:String ): Word;
var
i: Integer;
@ -294,6 +301,40 @@ begin
Result := LCLMenuItemInit(item, atitle, ShortCut(VKKey, State));
end;
function FindEditMenu(const menu: NSMenu; const title: NSString): NSMenuItem;
var
index: NSInteger;
begin
if NOT Assigned(menu) then
Exit;
index:= menu.indexOfItemWithTitle(title);
if index >= 0 then begin
Result:= menu.itemAtIndex(index);
Exit;
end;
Result:= FindEditMenuByKeyEquivalent(menu, NSSTR('c')); // Command+C
end;
procedure NSMenuAddItemsFromLCLMenu(menu: NSMenu; lclMenu: TMenuItem);
var
index: Integer;
lclItem: TMenuItem;
item: NSMenuItem;
begin
if NOT Assigned(menu) then
Exit;
if NOT Assigned(lclMenu) then
Exit;
for index:=0 to lclMenu.Count-1 do begin
lclItem:= lclMenu.Items[index];
item:= menuItemHandleCreateFunc(lclItem);
menu.addItem(item);
end;
end;
{ TCocoaMenu }
procedure TCocoaMenu.dealloc;
@ -328,8 +369,6 @@ begin
lNSSubmenu := NSMenu.alloc.initWithTitle(NSString.string_);
appleMenu.setSubmenu(lNSSubmenu);
lNSSubmenu.release;
appleMenu.attachAppleMenuItems();
end;
// For when there is a menu item with title
@ -350,6 +389,7 @@ procedure TCocoaMenu.attachAppleMenu();
begin
if attachedAppleMenu then Exit;
if appleMenu = nil then Exit;
appleMenu.attachAppleMenuItems();
attachedAppleMenu := True;
insertItem_atIndex(appleMenu, 0);
end;
@ -409,6 +449,24 @@ begin
Result := menuItemCallback.MenuItemTarget;
end;
function TCocoaMenuItem.FindSubmenuByLclItem(lclItem: TMenuItem): TCocoaMenuItem;
var
nsItem: NSMenuItem;
begin
Result:= nil;
if NOT Assigned(lclItem) then
Exit;
for nsItem in self.submenu.itemArray do begin
if NOT nsItem.isKindOfClass(TCocoaMenuItem) then
continue;
if TCocoaMenuItem(nsItem).FMenuItemTarget<>lclItem then
continue;
Result:= TCocoaMenuItem(nsItem);
break;
end;
end;
procedure TCocoaMenuItem.lclItemSelected(sender:id);
begin
menuItemCallback.ItemSelected;
@ -429,51 +487,101 @@ procedure TCocoaMenuItem.attachAppleMenuItems();
var
item : NSMenuItem;
itemSubMenu: NSMenu;
currentIndex: NSInteger = 0;
procedure attachSpecialMenuItem(
lclItem: TMenuItem;
title: String;
key: Word = 0;
state: TShiftState = [] );
var
keyEquivalent: NSString;
keyMask: NSUInteger;
begin
if NOT Assigned(lclItem) then
Exit;
item:= self.FindSubmenuByLclItem(lclItem);
if NOT Assigned(item) then begin
item:= menuItemHandleCreateFunc(lclItem);
submenu.insertItem_atIndex(item, currentIndex);
item.release;
inc(currentIndex);
submenu.insertItem_atIndex(NSMenuItem.separatorItem, currentIndex);
inc(currentIndex);
end;
item.setTitle( StrToNSString(title) );
item.setImage(nil);
ShortcutToKeyEquivalent(ShortCut(key,state), keyEquivalent, keyMask );
item.setKeyEquivalent(keyEquivalent);
item.setKeyEquivalentModifierMask(keyMask);
end;
begin
if attachedAppleMenuItems then Exit;
if not hasSubmenu() then Exit;
attachedAppleMenuItems := True;
if NOT Assigned(self.FMenuItemTarget) then
self.FMenuItemTarget:= macOS_AppMenuIntf.customMenus;
// APP Custom
NSMenuAddItemsFromLCLMenu(self.submenu, macOS_AppMenuIntf.customMenus);
// About
attachSpecialMenuItem(
macOS_AppMenuIntf.aboutItem,
Format(rsMacOSMenuAbout, [Application.Title]) );
// Preferences
attachSpecialMenuItem(
macOS_AppMenuIntf.preferencesItem,
rsMacOSMenuPreferences,
VK_OEM_COMMA, [ssMeta]);
// Auto Create App Menu below?
if macOS_AppMenuIntf.dontAutoCreateAppMenuItems then
Exit;
// Separator
submenu.insertItem_atIndex(NSMenuItem.separatorItem, submenu.itemArray.count);
submenu.addItem(NSMenuItem.separatorItem);
// Services
item := LCLMenuItemInit( TCocoaMenuItem.alloc, rsMacOSMenuServices);
item.setTarget(nil);
item.setAction(nil);
submenu.insertItem_atIndex(item, submenu.itemArray.count);
submenu.addItem(item);
itemSubMenu := NSMenu.alloc.initWithTitle( ControlTitleToNSStr(rsMacOSMenuServices));
item.setSubmenu(itemSubMenu);
NSApplication(NSApp).setServicesMenu(itemSubMenu);
itemSubMenu.release;
NSApplication(NSApp).setServicesMenu(item.submenu);
item.release;
// Separator
submenu.insertItem_atIndex(NSMenuItem.separatorItem, submenu.itemArray.count);
submenu.addItem(NSMenuItem.separatorItem);
// Hide App Meta-H
item := LCLMenuItemInit( TCocoaMenuItem_HideApp.alloc, Format(rsMacOSMenuHide, [Application.Title]), VK_H, [ssMeta]);
submenu.insertItem_atIndex(item, submenu.itemArray.count);
submenu.addItem(item);
item.release;
// Hide Others Meta-Alt-H
item := LCLMenuItemInit( TCocoaMenuItem_HideOthers.alloc, rsMacOSMenuHideOthers, VK_H, [ssMeta, ssAlt]);
submenu.insertItem_atIndex(item, submenu.itemArray.count);
submenu.addItem(item);
item.release;
// Show All
item := LCLMenuItemInit( TCocoaMenuItem_ShowAllApp.alloc, rsMacOSMenuShowAll);
submenu.insertItem_atIndex(item, submenu.itemArray.count);
submenu.addItem(item);
item.release;
// Separator
submenu.insertItem_atIndex(NSMenuItem.separatorItem, submenu.itemArray.count);
submenu.addItem(NSMenuItem.separatorItem);
// Quit Meta-Q
item := LCLMenuItemInit( TCocoaMenuItem_Quit.alloc, Format(rsMacOSMenuQuit, [Application.Title]), VK_Q, [ssMeta]);
submenu.insertItem_atIndex(item, submenu.itemArray.count);
submenu.addItem(item);
item.release;
attachedAppleMenuItems := True;
end;
function TCocoaMenuItem.isValidAppleMenu(): LCLObjCBoolean;
@ -606,10 +714,12 @@ begin
end;
initialization
macOS_AppMenuIntf:= TMacOS_AppMenuIntf.Create;
finalization
MenuTrackCancelAll;
if menuTrack <> nil then menuTrack.release;
FreeAndNil(macOS_AppMenuIntf);
end.

View File

@ -147,7 +147,6 @@ end;
class function TCocoaWSMainMenu.CreateHandle(const AMenu: TMenu): HMENU;
begin
Result := HMENU(AllocCocoaMenu);
TCocoaMenu(Result).createAppleMenu();
end;
{ TCocoaWSMenuItem }
@ -530,4 +529,13 @@ begin
APopupMenu.Close; // notify LCL popup menu
end;
function CreateMenuItemHandle(const AMenuItem: TMenuItem): NSMenuItem;
begin
Result:= NSMenuItem( TCocoaWSMenuItem.CreateHandle(AMenuItem) );
end;
initialization
CocoaMenus.menuItemHandleCreateFunc:= @CreateMenuItemHandle;
end.