diff --git a/src/Avalonia.Native/AvaloniaNativeMenuExporter.cs b/src/Avalonia.Native/AvaloniaNativeMenuExporter.cs index 8dd54f4423..14eb4b03ce 100644 --- a/src/Avalonia.Native/AvaloniaNativeMenuExporter.cs +++ b/src/Avalonia.Native/AvaloniaNativeMenuExporter.cs @@ -14,11 +14,18 @@ namespace Avalonia.Native.Interop public partial class IAvnAppMenuItem { private IAvnAppMenu _subMenu; + private AvaloniaNativeMenuExporter _exporter; public NativeMenuItemBase Managed { get; set; } - public void Update(IAvaloniaNativeFactory factory, NativeMenuItem item) + internal void Update(AvaloniaNativeMenuExporter exporter, IAvaloniaNativeFactory factory, NativeMenuItem item) { + _exporter = exporter; + + Managed = item; + + Managed.PropertyChanged += Item_PropertyChanged; + using (var buffer = new Utf8Buffer(item.Header)) { Title = buffer.DangerousGetHandle(); @@ -49,20 +56,35 @@ namespace Avalonia.Native.Interop _subMenu = factory.CreateMenu(); } - _subMenu.Update(factory, item.Menu); + _subMenu.Update(exporter, factory, item.Menu); } if (item.Menu == null && _subMenu != null) { + _subMenu.Remove(); + // todo remove submenu. // needs implementing on native side also. } } + + private void Item_PropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e) + { + _exporter.QueueReset(); + } + + internal void Remove() + { + _exporter = null; + Managed = null; + Managed.PropertyChanged -= Item_PropertyChanged; + } } public partial class IAvnAppMenu { + private AvaloniaNativeMenuExporter _exporter; private NativeMenu _menu; private List _menuItems = new List(); private Dictionary _menuItemLookup = new Dictionary(); @@ -71,10 +93,18 @@ namespace Avalonia.Native.Interop { _menuItemLookup.Remove(item.Managed); _menuItems.Remove(item); + item.Remove(); RemoveItem(item); } + internal void Remove() + { + ((INotifyCollectionChanged)_menu.Items).CollectionChanged -= IAvnAppMenu_CollectionChanged; + _exporter = null; + _menu = null; + } + private void InsertAt(int index, IAvnAppMenuItem item) { if (item.Managed == null) @@ -96,7 +126,7 @@ namespace Avalonia.Native.Interop return nativeItem; } - public void Update(IAvaloniaNativeFactory factory, NativeMenu menu, string title = "") + internal void Update(AvaloniaNativeMenuExporter exporter, IAvaloniaNativeFactory factory, NativeMenu menu, string title = "") { if (_menu == null) { @@ -107,6 +137,10 @@ namespace Avalonia.Native.Interop throw new Exception("Cannot update a menu from another instance"); } + _exporter = exporter; + + ((INotifyCollectionChanged)_menu.Items).CollectionChanged += IAvnAppMenu_CollectionChanged; + if (!string.IsNullOrWhiteSpace(title)) { using (var buffer = new Utf8Buffer(title)) @@ -134,7 +168,7 @@ namespace Avalonia.Native.Interop if (menu.Items[i] is NativeMenuItem nmi) { - nativeItem.Update(factory, nmi); + nativeItem.Update(exporter, factory, nmi); } } @@ -143,6 +177,11 @@ namespace Avalonia.Native.Interop _menuItems.Remove(_menuItems[i]); } } + + private void IAvnAppMenu_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + _exporter.QueueReset(); + } } } @@ -181,11 +220,10 @@ namespace Avalonia.Native class AvaloniaNativeMenuExporter : ITopLevelNativeMenuExporter { private IAvaloniaNativeFactory _factory; - private NativeMenu _menu; private bool _resetQueued; private bool _exported = false; private IAvnWindow _nativeWindow; - private List _menuItems = new List(); + private NativeMenu _menu; public AvaloniaNativeMenuExporter(IAvnWindow nativeWindow, IAvaloniaNativeFactory factory) { @@ -199,8 +237,6 @@ namespace Avalonia.Native { _factory = factory; - _menu = NativeMenu.GetMenu(Application.Current); - DoLayoutReset(); } @@ -210,13 +246,8 @@ namespace Avalonia.Native public void SetNativeMenu(NativeMenu menu) { - if (menu == null) - menu = new NativeMenu(); - - if (_menu != null) - ((INotifyCollectionChanged)_menu.Items).CollectionChanged -= OnMenuItemsChanged; - _menu = menu; - ((INotifyCollectionChanged)_menu.Items).CollectionChanged += OnMenuItemsChanged; + if (_menu == null) + _menu = new NativeMenu(); DoLayoutReset(); } @@ -244,50 +275,29 @@ namespace Avalonia.Native return result; } - private void OnItemPropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e) - { - QueueReset(); - } - - private void OnMenuItemsChanged(object sender, NotifyCollectionChangedEventArgs e) - { - QueueReset(); - } - void DoLayoutReset() { _resetQueued = false; - foreach (var i in _menuItems) - { - i.PropertyChanged -= OnItemPropertyChanged; - if (i.Menu != null) - ((INotifyCollectionChanged)i.Menu.Items).CollectionChanged -= OnMenuItemsChanged; - } - - _menuItems.Clear(); if (_nativeWindow is null) { - _menu = NativeMenu.GetMenu(Application.Current); + var appMenu = NativeMenu.GetMenu(Application.Current); - if (_menu != null) - { - SetMenu(_menu); - } - else + if (appMenu == null) { - SetMenu(CreateDefaultAppMenu()); + appMenu = CreateDefaultAppMenu(); + SetMenu(appMenu); } } else { - SetMenu(_nativeWindow, _menu?.Items); + SetMenu(_nativeWindow, _menu); } _exported = true; } - private void QueueReset() + internal void QueueReset() { if (_resetQueued) return; @@ -295,27 +305,6 @@ namespace Avalonia.Native Dispatcher.UIThread.Post(DoLayoutReset, DispatcherPriority.Background); } - private IAvnAppMenu CreateSubmenu(ICollection children) - { - var menu = _factory.CreateMenu(); - - //SetChildren(menu, children); - - return menu; - } - - private void AddMenuItem(NativeMenuItem item) - { - if (item.Menu?.Items != null) - { - ((INotifyCollectionChanged)item.Menu.Items).CollectionChanged += OnMenuItemsChanged; - } - } - - - - - private void SetMenu(NativeMenu menu) { var appMenu = _factory.ObtainAppMenu(); @@ -323,41 +312,23 @@ namespace Avalonia.Native if (appMenu is null) { appMenu = _factory.CreateMenu(); + _factory.SetAppMenu(appMenu); } - var menuItem = menu.Parent; - - if (menu.Parent is null) - { - menuItem = new NativeMenuItem(); - } - - menuItem.Menu = menu; - - //appMenu.Clear(); - //AddItemsToMenu(appMenu, new List { menuItem }); - - _factory.SetAppMenu(appMenu); + appMenu.Update(this, _factory, menu); } - private void SetMenu(IAvnWindow avnWindow, ICollection menuItems) + private void SetMenu(IAvnWindow avnWindow, NativeMenu menu) { - if (menuItems is null) - { - menuItems = new List(); - } - var appMenu = avnWindow.ObtainMainMenu(); if (appMenu is null) { appMenu = _factory.CreateMenu(); + avnWindow.SetMainMenu(appMenu); } - //appMenu.Clear(); - //AddItemsToMenu(appMenu, menuItems); - - avnWindow.SetMainMenu(appMenu); + appMenu.Update(this, _factory, menu); } } }