Как создать меню подпунктов под именем приложения на OSX?

Как добавить TMenuItem под Project1 и выше Выйти на снимке экрана ниже?

введите здесь описание изображения

Я создал TMenuBar с проверенным свойством UseOSMenu. Первый TMenuItem, который я добавил, является вторым на главной панели...


person Whiler    schedule 03.10.2011    source источник


Ответы (3)


Это можно сделать, назначив TMenuBar класса реализации IItemsContainer свойству Application.ApplicationMenuItems.

Пример:

Если бы в форме был компонент строки меню с именем MenuBar1, то вы бы просто вызвали следующее в конструкторе форм (или OnCreate).

Application.ApplicationMenuItems := Menubar1;

Затем у вас может быть второй компонент TMenuBar для определения других пунктов меню.

Я бы указал вам на вики-тему по свойству ApplicationMenuItems, но в ней нет дополнительной помощи...

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

person jed    schedule 11.10.2011
comment
В XE7 этого свойства нет, как и в TApplication. Может быть, его куда-то перенесли? - person Jerry Dodge; 19.12.2014

Я создал модуль, чтобы попытаться управлять тем, что я хотел бы... С его помощью я могу использовать определенный TMenuItem... и перемещать его подэлементы в подменю приложения... (я все еще не не знаю, как добавить с нуля...)

Я также использую ответ из Мехмед Али управлять сепараторами...

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.

Он работает так, как ожидалось, и это то, что я хочу на OSX...

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

но теперь у меня проблема с Windows, потому что, что бы я ни пытался, у меня все еще отображается меню, которое я добавил для Mac, под Windows... ;o(

person Whiler    schedule 04.10.2011
comment
Дополнительный ifdef должен позаботиться об этом - person Johan; 07.10.2011
comment
@Johan: вокруг чего? ;o( Единственный способ, который я нашел, - это создавать элементы меню только в том случае, если они на OSX... но это означает их динамическое создание в исходном коде, а не в dfm... что менее удобно для пользователя... Это то, что вы имеете в виду, или у вас есть другая идея? - person Whiler; 07.10.2011
comment
Моя идея состояла в том, чтобы сделать ifdef отдельным исходным кодом для OSX и Windows. два совершенно разных дерева. Обратите внимание, что изменение системного меню в Windows происходит очень редко и вообще не рекомендуется, поэтому обычно вы делаете это в меню file. - person Johan; 07.10.2011
comment
хммм... понятно... я могу создать 2 разных модуля данных... по одному для каждой платформы и использовать хороший с ifdef... хорошая идея, спасибо! - person Whiler; 07.10.2011

Начиная с Delphi XE7, свойство Application.ApplicationMenuItems больше не существует.

Теперь вам нужно создать элемент TMainMenu, чтобы получить ожидаемый результат, не нужно его назначать. Просто поместите TMainMenu в свою основную форму и добавьте свои элементы, и они появятся в меню приложения OSX.

person Nick C    schedule 27.01.2015