From 89e5f3888d6cbe7778fe56013c738fc82a7d765f Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 17 Mar 2021 12:26:44 +0100 Subject: [PATCH 1/2] Added Opening/Closed for NativeMenu on OSX. Adds the `Opening` and `Closed` events for OSX to `NativeMenu` which relate to `menuWillOpen` and `menuDidClose`. Note that `NativeMenu` already exposed `NeedsUpdate` as `Opening`; this event has been moved to a separate `NeedsUpdate` event. --- native/Avalonia.Native/src/OSX/menu.h | 5 +-- native/Avalonia.Native/src/OSX/menu.mm | 26 +++++++++++++++ .../INativeMenuExporterEventsImplBridge.cs | 2 ++ src/Avalonia.Controls/NativeMenu.cs | 33 ++++++++++++++++++- src/Avalonia.Native/IAvnMenu.cs | 22 +++++++++++++ src/Avalonia.Native/avn.idl | 5 ++- 6 files changed, 87 insertions(+), 6 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/menu.h b/native/Avalonia.Native/src/OSX/menu.h index 564fdc132b..ea68b354bc 100644 --- a/native/Avalonia.Native/src/OSX/menu.h +++ b/native/Avalonia.Native/src/OSX/menu.h @@ -60,7 +60,6 @@ public: void RaiseOnClicked(); }; - class AvnAppMenu : public ComSingleObject { private: @@ -71,10 +70,12 @@ public: FORWARD_IUNKNOWN() AvnAppMenu(IAvnMenuEvents* events); - + AvnMenu* GetNative(); void RaiseNeedsUpdate (); + void RaiseOpening(); + void RaiseClosed(); virtual HRESULT InsertItem (int index, IAvnMenuItem* item) override; diff --git a/native/Avalonia.Native/src/OSX/menu.mm b/native/Avalonia.Native/src/OSX/menu.mm index ea5cca9ce8..c33a447fcc 100644 --- a/native/Avalonia.Native/src/OSX/menu.mm +++ b/native/Avalonia.Native/src/OSX/menu.mm @@ -298,6 +298,23 @@ void AvnAppMenu::RaiseNeedsUpdate() } } +void AvnAppMenu::RaiseOpening() +{ + if(_baseEvents != nullptr) + { + _baseEvents->Opening(); + } +} + +void AvnAppMenu::RaiseClosed() +{ + if(_baseEvents != nullptr) + { + _baseEvents->Closed(); + } +} + + HRESULT AvnAppMenu::InsertItem(int index, IAvnMenuItem *item) { @autoreleasepool @@ -382,6 +399,15 @@ HRESULT AvnAppMenu::Clear() _parent->RaiseNeedsUpdate(); } +- (void)menuWillOpen:(NSMenu *)menu +{ + _parent->RaiseOpening(); +} + +- (void)menuDidClose:(NSMenu *)menu +{ + _parent->RaiseClosed(); +} @end diff --git a/src/Avalonia.Controls/INativeMenuExporterEventsImplBridge.cs b/src/Avalonia.Controls/INativeMenuExporterEventsImplBridge.cs index 672d5c1a13..f492e6ca0f 100644 --- a/src/Avalonia.Controls/INativeMenuExporterEventsImplBridge.cs +++ b/src/Avalonia.Controls/INativeMenuExporterEventsImplBridge.cs @@ -3,5 +3,7 @@ namespace Avalonia.Controls public interface INativeMenuExporterEventsImplBridge { void RaiseNeedsUpdate (); + void RaiseOpening(); + void RaiseClosed(); } } diff --git a/src/Avalonia.Controls/NativeMenu.cs b/src/Avalonia.Controls/NativeMenu.cs index 38a9f03d29..58ee99722f 100644 --- a/src/Avalonia.Controls/NativeMenu.cs +++ b/src/Avalonia.Controls/NativeMenu.cs @@ -12,13 +12,34 @@ namespace Avalonia.Controls private readonly AvaloniaList _items = new AvaloniaList { ResetBehavior = ResetBehavior.Remove }; private NativeMenuItem _parent; + [Content] public IList Items => _items; /// - /// Raised when the user clicks the menu and before its opened. Use this event to update the menu dynamically. + /// Raised when the menu requests an update. + /// + /// + /// Use this event to add, remove or modify menu items before a menu is + /// shown or a hotkey is pressed. + /// + public event EventHandler NeedsUpdate; + + /// + /// Raised before the menu is opened. /// + /// + /// Do not update the menu in this event; use . + /// public event EventHandler Opening; + + /// + /// Raised after the menu is closed. + /// + /// + /// Do not update the menu in this event; use . + /// + public event EventHandler Closed; public NativeMenu() { @@ -27,10 +48,20 @@ namespace Avalonia.Controls } void INativeMenuExporterEventsImplBridge.RaiseNeedsUpdate() + { + NeedsUpdate?.Invoke(this, EventArgs.Empty); + } + + void INativeMenuExporterEventsImplBridge.RaiseOpening() { Opening?.Invoke(this, EventArgs.Empty); } + void INativeMenuExporterEventsImplBridge.RaiseClosed() + { + Closed?.Invoke(this, EventArgs.Empty); + } + private void Validator(NativeMenuItemBase obj) { if (obj.Parent != null) diff --git a/src/Avalonia.Native/IAvnMenu.cs b/src/Avalonia.Native/IAvnMenu.cs index dd9464284f..cba59c481d 100644 --- a/src/Avalonia.Native/IAvnMenu.cs +++ b/src/Avalonia.Native/IAvnMenu.cs @@ -20,11 +20,23 @@ namespace Avalonia.Native.Interop { _parent?.RaiseNeedsUpdate(); } + + public void Opening() + { + _parent?.RaiseOpening(); + } + + public void Closed() + { + _parent?.RaiseClosed(); + } } partial interface IAvnMenu { void RaiseNeedsUpdate(); + void RaiseOpening(); + void RaiseClosed(); void Deinitialise(); } } @@ -45,6 +57,16 @@ namespace Avalonia.Native.Interop.Impl _exporter.UpdateIfNeeded(); } + public void RaiseOpening() + { + (ManagedMenu as INativeMenuExporterEventsImplBridge).RaiseOpening(); + } + + public void RaiseClosed() + { + (ManagedMenu as INativeMenuExporterEventsImplBridge).RaiseClosed(); + } + internal NativeMenu ManagedMenu { get; private set; } public static __MicroComIAvnMenuProxy Create(IAvaloniaNativeFactory factory) diff --git a/src/Avalonia.Native/avn.idl b/src/Avalonia.Native/avn.idl index 2693f5f139..af39dd8c2f 100644 --- a/src/Avalonia.Native/avn.idl +++ b/src/Avalonia.Native/avn.idl @@ -685,10 +685,9 @@ interface IAvnMenuItem : IUnknown [uuid(0af7df53-7632-42f4-a650-0992c361b477)] interface IAvnMenuEvents : IUnknown { - /** - * NeedsUpdate - */ void NeedsUpdate(); + void Opening(); + void Closed(); } [uuid(5142bb41-66ab-49e7-bb37-cd079c000f27)] From 0b16629e9bc05af7e4da7ff47d4dba85bfd0ba92 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 17 Mar 2021 14:53:16 +0100 Subject: [PATCH 2/2] Update apicompat baseline. --- src/Avalonia.Controls/ApiCompatBaseline.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/ApiCompatBaseline.txt b/src/Avalonia.Controls/ApiCompatBaseline.txt index aa8db78087..0284463f1c 100644 --- a/src/Avalonia.Controls/ApiCompatBaseline.txt +++ b/src/Avalonia.Controls/ApiCompatBaseline.txt @@ -1,7 +1,9 @@ Compat issues with assembly Avalonia.Controls: +InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.INativeMenuExporterEventsImplBridge.RaiseClosed()' is present in the implementation but not in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.INativeMenuExporterEventsImplBridge.RaiseOpening()' is present in the implementation but not in the contract. MembersMustExist : Member 'public void Avalonia.Controls.Embedding.Offscreen.OffscreenTopLevelImplBase.SetCursor(Avalonia.Platform.IPlatformHandle)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.AvaloniaProperty Avalonia.AvaloniaProperty Avalonia.Controls.Notifications.NotificationCard.CloseOnClickProperty' does not exist in the implementation but it does exist in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.ICursorImpl)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.IPlatformHandle)' is present in the contract but not in the implementation. MembersMustExist : Member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.IPlatformHandle)' does not exist in the implementation but it does exist in the contract. -Total Issues: 5 +Total Issues: 7