Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create subitems menus under the application name on OSX?

How to add TMenuItem under Project1 and above Quit on the screenshot below?

enter image description here

I have created a TMenuBar with property UseOSMenu checked. The first TMenuItem I added is the second one in the main bar...

like image 784
Whiler Avatar asked Oct 03 '11 08:10

Whiler


3 Answers

You can do this by assigning a TMenuBar of IItemsContainer implementing class to the Application.ApplicationMenuItems property.

Example:

If there was a menu bar component on the form called MenuBar1, then you would just call the following in your forms constructor (or OnCreate).

Application.ApplicationMenuItems := Menubar1;

You can then have a second TMenuBar component to define the other menu items.

I'd point you to the wiki topic on the ApplicationMenuItems property, but it has no additional help...

http://docwiki.embarcadero.com/VCL/XE2/en/FMX.Forms.TApplication.ApplicationMenuItems

like image 126
jed Avatar answered Nov 14 '22 17:11

jed


I have created a unit to try to manage what I would like... With it, I can use a specific TMenuItem... and move its subitems to the application submenu... (I still don't know how to add one from scratch...)

I also use the answer from Mehmed Ali to manage the separators...

unit uMenu;

interface

uses
FMX.Dialogs, System.SysUtils,
  FMX.Menus
{$IFDEF MACOS}
  ,Macapi.ObjectiveC,MacApi.AppKit,MacApi.Foundation,FMX.Platform.Mac
{$ENDIF}
  ;

  type
    ManageMenu = class
  private
{$IFDEF MACOS}
    class procedure FixSeparatorItemsForMenuItem (MenuItem: NSMenuItem);
    class procedure MoveItemsToMacApplicationMenu(source, target: NSMenuItem); overload;
    class procedure MoveItemsToMacApplicationMenu(index: Integer);             overload;
{$ENDIF}
  public
    class procedure FixSeparatorItemsForMac;
    class procedure MoveItemsToMacApplicationMenu(index: Integer; menu: TMainMenu); overload;
    class procedure MoveItemsToMacApplicationMenu(index: Integer; menu: TMenuBar);  overload;
  end;

implementation

{ ManageMenu }

{$IFDEF MACOS}
class procedure ManageMenu.FixSeparatorItemsForMenuItem(MenuItem:NSMenuItem);
var
  i      : Integer;
  subItem: NSMenuItem;
begin
  if (MenuItem.hasSubmenu = False) then exit;

  for i := 0 to Pred(MenuItem.submenu.itemArray.count) do
  begin
    subItem := MenuItem.submenu.itemAtIndex(i);
    if (subItem.title.isEqualToString(NSSTR('-'))= True) then
    begin
      MenuItem.submenu.removeItemAtIndex(i);
      MenuItem.submenu.insertItem(TNSMenuItem.Wrap(TNSMenuItem.OCClass.separatorItem), i);
    end
    else
    begin
      FixSeparatorItemsForMenuItem(subItem);
    end;
  end;
end;
{$ENDIF}

class procedure ManageMenu.FixSeparatorItemsForMac;
{$IFDEF MACOS}
var
  NSApp   : NSApplication;
  MainMenu: NSMenu;
  AppItem : NSMenuItem;
  i       : Integer;
{$ENDIF}
begin
{$IFDEF MACOS}
  NSApp    := TNSApplication.Wrap(TNSApplication.OCClass.sharedApplication);
  MainMenu := NSApp.mainMenu;
  if (MainMenu <> nil) then
  begin
    for i := 0 to Pred(MainMenu.itemArray.Count) do
    begin
      AppItem := mainMenu.itemAtIndex(i);
      FixSeparatorItemsForMenuItem(AppItem);
    end;
  end;
{$ENDIF}
end;

{$IFDEF MACOS}
class procedure ManageMenu.MoveItemsToMacApplicationMenu(source, target: NSMenuItem);
var
  iLoop, iMax: Integer;
  subItem    : NSMenuItem;
begin
  if (source.hasSubmenu = False) then exit;

  iMax := Pred(source.submenu.itemArray.count);
  for iLoop := iMax downto 0 do
  begin
    subItem := source.submenu.itemAtIndex(iLoop);
    source.submenu.removeItemAtIndex(iLoop);
    target.submenu.insertItem(subItem, 0);
  end;
  // Hide the parent
  source.setHidden(True);
end;
{$ENDIF}

{$IFDEF MACOS}
class procedure ManageMenu.MoveItemsToMacApplicationMenu(index: Integer);
var
  NSApp   : NSApplication;
  MainMenu: NSMenu;
  source  : NSMenuItem;
  target  : NSMenuItem;
begin
  NSApp    := TNSApplication.Wrap(TNSApplication.OCClass.sharedApplication);
  MainMenu := NSApp.mainMenu;
  if (MainMenu <> nil) then
  begin
    begin
      if (MainMenu.itemArray.count > 1) then
      begin
        source := mainMenu.itemAtIndex(Succ(index));
        target := mainMenu.itemAtIndex(0);
        MoveItemsToMacApplicationMenu(source, target);
      end;
    end;
  end;
end;
{$ENDIF}

class procedure ManageMenu.MoveItemsToMacApplicationMenu(index: Integer; menu: TMainMenu);
begin
{$IFDEF MACOS}
  MoveItemsToMacApplicationMenu(index);
{$ELSE}
//  (menu.Children[Succ(index)] as TMenuItem).Visible := False;
//  menu.RemoveObject(...);
// ... I don't knwo how to remove items on Windows ;o(((
{$ENDIF}
end;

class procedure ManageMenu.MoveItemsToMacApplicationMenu(index: Integer; menu: TMenuBar);
begin
{$IFDEF MACOS}
  MoveItemsToMacApplicationMenu(index);
{$ELSE}
  if (menu.ChildrenCount > Succ(index)) and (menu.Children[Succ(index)] is TMenuItem) then
  begin
//  (menu.Children[Succ(index)] as TMenuItem).Visible := False;
//  menu.RemoveObject(...);
// ... I don't knwo how to remove items on Windows ;o(((

//    menu.BeginUpdate;
//    menu.RemoveObject((menu.Children[Succ(index)] as TMenuItem));
//    menu.RemoveFreeNotify((menu.Children[Succ(index)] as TMenuItem));
//    menu.DeleteChildren;
//    (menu.Children[Succ(index)] as TMenuItem).View.Visible := False;
//    .Free;
//    (menu.Children[Succ(index)] as TMenuItem).Destroy;
//    menu.EndUpdate;
  end;
{$ENDIF}
end;

end.

It works as expected and it is what I want on OSX...

procedure TfrmMain.FormActivate(Sender: TObject);
begin
  if not bAlreadyActivated then
  begin
    bAlreadyActivated := True;
    ManageMenu.FixSeparatorItemsForMac;
    ManageMenu.MoveItemsToMacApplicationMenu(0, MainMenu1);
  end;
end;

but now, I have an issue on Windows because whatever I try, I still have the menu I added for Mac displayed under Windows... ;o(

like image 25
Whiler Avatar answered Nov 14 '22 17:11

Whiler


As of Delphi XE7, the Application.ApplicationMenuItems property no longer exists.

You now have to create a TMainMenu item to get the expected result, no need to assign it. Just drop a TMainMenu on to your main form and add your items, and they will appear in the OSX application menu.

like image 40
Sierra C Avatar answered Nov 14 '22 17:11

Sierra C