【问题标题】:How to create subitems menus under the application name on OSX?如何在 OSX 上的应用程序名称下创建子项菜单?
【发布时间】:2011-10-03 08:50:01
【问题描述】:

如何在Project1及以上Quit下面的截图中添加TMenuItem?

我创建了一个 TMenuBar 并检查了 UseOSMenu 属性。 我添加的第一个 TMenuItem 是主栏中的第二个...

【问题讨论】:

    标签: macos delphi menuitem delphi-xe2 firemonkey


    【解决方案1】:

    您可以通过将 IItemsContainer 实现类的 TMenuBar 分配给 Application.ApplicationMenuItems 属性来做到这一点。

    例子:

    如果表单上有一个名为 MenuBar1 的菜单栏组件,那么您只需在表单构造函数(或 OnCreate)中调用以下内容。

    Application.ApplicationMenuItems := Menubar1;

    然后您可以使用第二个 TMenuBar 组件来定义其他菜单项。

    我会向您指出有关 ApplicationMenuItems 属性的 wiki 主题,但它没有其他帮助...

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

    【讨论】:

    • 在 XE7 中这个属性不存在,TApplication 上也没有类似的东西。也许它被搬到了别处?
    【解决方案2】:

    我创建了一个单元来尝试管理我想要的... 有了它,我可以使用特定的 TMenuItem...并将其子项移动到应用程序子菜单...(我仍然不知道如何从头开始添加...)

    我还使用Mehmed Ali 的答案来管理分隔符...

    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 上遇到了问题,因为无论我尝试什么,我仍然在 Windows 下显示为 Mac 添加的菜单... ;o(

    【讨论】:

    • 一个额外的 ifdef 应该可以解决这个问题
    • @Johan:围绕什么? ;o( 我发现的唯一方法是仅在 osx 上创建菜单项...但是,这意味着在源代码中而不是在 dfm 中动态创建它们...这对用户不太友好... . 是你的意思还是你有别的想法?
    • 我的想法是为 OSX 和 Windows 提供一个单独的源。两棵完全不同的树。请注意,在 Windows 中更改系统菜单是非常罕见的事情,根本不推荐,因此通常您会在 file 菜单下进行。
    • hummm.. 我明白了.. 我可以创建 2 个不同的数据模块.. 为每个平台使用一个,并使用带有 ifdef 的好的一个...好主意,谢谢!
    【解决方案3】:

    从 Delphi XE7 开始,Application.ApplicationMenuItems 属性不再存在。

    您现在必须创建一个TMainMenu 项目以获得预期结果,无需分配它。只需将TMainMenu 拖放到您的主窗体并添加您的项目,它们就会出现在 OSX 应用程序菜单中。

    【讨论】:

      猜你喜欢
      • 2015-01-05
      • 1970-01-01
      • 1970-01-01
      • 2014-07-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多