diff --git a/native/Avalonia.Native/inc/avalonia-native.h b/native/Avalonia.Native/inc/avalonia-native.h index cbd90e1dcf..7d9b89852e 100644 --- a/native/Avalonia.Native/inc/avalonia-native.h +++ b/native/Avalonia.Native/inc/avalonia-native.h @@ -175,6 +175,8 @@ public: virtual HRESULT CreateClipboard(IAvnClipboard** ppv) = 0; virtual HRESULT CreateCursorFactory(IAvnCursorFactory** ppv) = 0; virtual HRESULT ObtainGlFeature(IAvnGlFeature** ppv) = 0; + virtual HRESULT ObtainAppMenu(IAvnAppMenu** retOut) = 0; + virtual HRESULT SetAppMenu(IAvnAppMenu* menu) = 0; virtual HRESULT CreateMenu (IAvnAppMenu** ppv) = 0; virtual HRESULT CreateMenuItem (IAvnAppMenuItem** ppv) = 0; }; diff --git a/native/Avalonia.Native/src/OSX/common.h b/native/Avalonia.Native/src/OSX/common.h index ccd0a5a9b9..91a1ba51c3 100644 --- a/native/Avalonia.Native/src/OSX/common.h +++ b/native/Avalonia.Native/src/OSX/common.h @@ -21,6 +21,9 @@ extern IAvnGlFeature* GetGlFeature(); extern IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(NSWindow* window, NSView* view); extern IAvnAppMenu* CreateAppMenu(); extern IAvnAppMenuItem* CreateAppMenuItem(); +extern void SetAppMenu (IAvnAppMenu* appMenu); +extern IAvnAppMenu* GetAppMenu (); +extern NSMenuItem* GetAppMenuItem (); extern void InitializeAvnApp(); extern NSApplicationActivationPolicy AvnDesiredActivationPolicy; diff --git a/native/Avalonia.Native/src/OSX/main.mm b/native/Avalonia.Native/src/OSX/main.mm index bdea26b761..ade077f00b 100644 --- a/native/Avalonia.Native/src/OSX/main.mm +++ b/native/Avalonia.Native/src/OSX/main.mm @@ -236,6 +236,24 @@ public: *ppv = ::CreateAppMenuItem(); return S_OK; } + + virtual HRESULT SetAppMenu (IAvnAppMenu* appMenu) override + { + ::SetAppMenu(appMenu); + return S_OK; + } + + virtual HRESULT ObtainAppMenu(IAvnAppMenu** retOut) override + { + if(retOut == nullptr) + { + return E_POINTER; + } + + *retOut = ::GetAppMenu(); + + return S_OK; + } }; extern "C" IAvaloniaNativeFactory* CreateAvaloniaNative() diff --git a/native/Avalonia.Native/src/OSX/menu.mm b/native/Avalonia.Native/src/OSX/menu.mm index ca03c50e59..9aec33d3db 100644 --- a/native/Avalonia.Native/src/OSX/menu.mm +++ b/native/Avalonia.Native/src/OSX/menu.mm @@ -189,3 +189,34 @@ extern IAvnAppMenuItem* CreateAppMenuItem() return new AvnAppMenuItem(); } } + +static IAvnAppMenu* s_appMenu = nullptr; +static NSMenuItem* s_appMenuItem = nullptr; + +extern void SetAppMenu (IAvnAppMenu* appMenu) +{ + s_appMenu = appMenu; + + if(s_appMenu != nullptr) + { + auto nativeMenu = dynamic_cast(s_appMenu); + + s_appMenuItem = [nativeMenu->GetNative() itemAtIndex:0]; + } + else + { + s_appMenuItem = nullptr; + } +} + +extern IAvnAppMenu* GetAppMenu () +{ + return s_appMenu; +} + +extern NSMenuItem* GetAppMenuItem () +{ + return s_appMenuItem; +} + + diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index 78ba930c9c..dbb437243a 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -1073,6 +1073,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent bool _canBecomeKeyAndMain; bool _closed; NSMenu* _menu; + bool _isAppMenuApplied; } - (void)dealloc @@ -1104,7 +1105,22 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent } _menu = menu; - [NSApp setMenu:menu]; + + if ([self isKeyWindow]) + { + auto appMenu = ::GetAppMenuItem(); + + if(appMenu != nullptr) + { + [[appMenu menu] removeItem:appMenu]; + + [_menu insertItem:appMenu atIndex:0]; + + _isAppMenuApplied = true; + } + + [NSApp setMenu:menu]; + } } -(void) setCanBecomeKeyAndMain @@ -1204,6 +1220,17 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent _menu = [NSMenu new]; } + auto appMenu = ::GetAppMenuItem(); + + if(appMenu != nullptr) + { + [[appMenu menu] removeItem:appMenu]; + + [_menu insertItem:appMenu atIndex:0]; + + _isAppMenuApplied = true; + } + [NSApp setMenu:_menu]; _parent->BaseEvents->Activated(); @@ -1251,7 +1278,27 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent if(_parent) _parent->BaseEvents->Deactivated(); - [NSApp setMenu:nullptr]; + auto appMenuItem = ::GetAppMenuItem(); + + if(appMenuItem != nullptr) + { + auto appMenu = ::GetAppMenu(); + + auto nativeAppMenu = dynamic_cast(appMenu); + + [[appMenuItem menu] removeItem:appMenuItem]; + + [nativeAppMenu->GetNative() addItem:appMenuItem]; + + [NSApp setMenu:nativeAppMenu->GetNative()]; + } + else + { + [NSApp setMenu:nullptr]; + } + + // remove window menu items from appmenu? + [super resignKeyWindow]; } diff --git a/src/Avalonia.Native/AvaloniaNativeMenuExporter.cs b/src/Avalonia.Native/AvaloniaNativeMenuExporter.cs index 1ae4a783d7..40159077d5 100644 --- a/src/Avalonia.Native/AvaloniaNativeMenuExporter.cs +++ b/src/Avalonia.Native/AvaloniaNativeMenuExporter.cs @@ -58,6 +58,14 @@ namespace Avalonia.Native DoLayoutReset(); } + public AvaloniaNativeMenuExporter(IAvaloniaNativeFactory factory) + { + _factory = factory; + + _menu = NativeMenu.GetMenu(Application.Current); + DoLayoutReset(); + } + public bool IsNativeMenuExported => _exported; public event EventHandler OnIsNativeMenuExportedChanged; @@ -109,7 +117,14 @@ namespace Avalonia.Native _menuItems.Clear(); - SetMenu(_nativeWindow, _menu?.Items); + if(_nativeWindow is null) + { + SetMenu(_menu?.Items); + } + else + { + SetMenu(_nativeWindow, _menu?.Items); + } _exported = true; } @@ -236,7 +251,7 @@ namespace Avalonia.Native } } - private void SetMenu(IAvnWindow avnWindow, ICollection menuItems) + private void SetMenu(ICollection menuItems) { if (menuItems is null) { @@ -247,11 +262,24 @@ namespace Avalonia.Native if (menu != null) { - var items = menuItems.ToList(); + var appMenu = _factory.ObtainAppMenu (); - items.InsertRange(0, menu.Items); + if (appMenu is null) + { + appMenu = _factory.CreateMenu(); + } - menuItems = items; + AddItemsToMenu(appMenu, menuItems); + + _factory.SetAppMenu(appMenu); + } + } + + private void SetMenu(IAvnWindow avnWindow, ICollection menuItems) + { + if (menuItems is null) + { + menuItems = new List(); } var appMenu = avnWindow.ObtainMainMenu(); @@ -259,13 +287,11 @@ namespace Avalonia.Native if (appMenu is null) { appMenu = _factory.CreateMenu(); - - avnWindow.SetMainMenu(appMenu); } - appMenu.Clear(); - AddItemsToMenu(appMenu, menuItems); + + avnWindow.SetMainMenu(appMenu); } } } diff --git a/src/Avalonia.Native/AvaloniaNativePlatform.cs b/src/Avalonia.Native/AvaloniaNativePlatform.cs index 3b26b6a60a..ddb71b61bb 100644 --- a/src/Avalonia.Native/AvaloniaNativePlatform.cs +++ b/src/Avalonia.Native/AvaloniaNativePlatform.cs @@ -57,7 +57,12 @@ namespace Avalonia.Native return Initialize(CreateAvaloniaNative(), options); } - public void SetupApplicationName() + public void SetupApplicationMenuExporter () + { + var exporter = new AvaloniaNativeMenuExporter(_factory); + } + + public void SetupApplicationName () { if(!string.IsNullOrWhiteSpace(Application.Current.Name)) { diff --git a/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs b/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs index df7b00ddd8..a22777d5eb 100644 --- a/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs +++ b/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs @@ -20,6 +20,7 @@ namespace Avalonia builder.AfterSetup (x=> { + platform.SetupApplicationMenuExporter(); platform.SetupApplicationName(); }); });