diff --git a/src/Avalonia.Native/AvaloniaNativeMenuExporter.cs b/src/Avalonia.Native/AvaloniaNativeMenuExporter.cs index 1c36510817..281af31bc2 100644 --- a/src/Avalonia.Native/AvaloniaNativeMenuExporter.cs +++ b/src/Avalonia.Native/AvaloniaNativeMenuExporter.cs @@ -15,8 +15,7 @@ namespace Avalonia.Native private bool _exported = false; private IAvnWindow _nativeWindow; private NativeMenu _menu; - private IAvnAppMenu _nativeMenu; - private IDisposable _disposable; + private IAvnAppMenu _nativeMenu; public AvaloniaNativeMenuExporter(IAvnWindow nativeWindow, IAvaloniaNativeFactory factory) { @@ -109,6 +108,8 @@ namespace Avalonia.Native if (_nativeMenu is null) { _nativeMenu = _factory.CreateMenu(); + + _nativeMenu.Initialise(menu, ""); } } @@ -131,9 +132,8 @@ namespace Avalonia.Native menuItem.Menu = menu; var setMenu = _nativeMenu.ManagedMenu != appMenuHolder; - - _disposable?.Dispose(); - _disposable = _nativeMenu.Update(this, _factory, appMenuHolder); + + _nativeMenu.Update(_factory, appMenuHolder); if (setMenu) { @@ -150,11 +150,12 @@ namespace Avalonia.Native if (_nativeMenu is null) { _nativeMenu = _factory.CreateMenu(); + + _nativeMenu.Initialise(menu, ""); } } - - _disposable?.Dispose(); - _disposable = _nativeMenu.Update(this, _factory, menu); + + _nativeMenu.Update(_factory, menu); avnWindow.SetMainMenu(_nativeMenu); } diff --git a/src/Avalonia.Native/IAvnAppMenu.cs b/src/Avalonia.Native/IAvnAppMenu.cs index 412980c2c4..94e48cdff1 100644 --- a/src/Avalonia.Native/IAvnAppMenu.cs +++ b/src/Avalonia.Native/IAvnAppMenu.cs @@ -9,9 +9,9 @@ namespace Avalonia.Native.Interop { public partial class IAvnAppMenu { - private AvaloniaNativeMenuExporter _exporter; private List _menuItems = new List(); private Dictionary _menuItemLookup = new Dictionary(); + private CompositeDisposable _propertyDisposables = new CompositeDisposable(); internal NativeMenu ManagedMenu { get; private set; } @@ -19,12 +19,9 @@ namespace Avalonia.Native.Interop { _menuItemLookup.Remove(item.ManagedMenuItem); _menuItems.Remove(item); - - item.Update(null, null, null); RemoveItem(item); item.Deinitialise(); - item.Dispose(); } @@ -41,7 +38,7 @@ namespace Avalonia.Native.Interop { var result = CreateNew(factory, item); - result.Initialise(); + result.Initialise(item); _menuItemLookup.Add(result.ManagedMenuItem, result); _menuItems.Insert(index, result); @@ -59,25 +56,12 @@ namespace Avalonia.Native.Interop return nativeItem; } - internal IDisposable Update(AvaloniaNativeMenuExporter exporter, IAvaloniaNativeFactory factory, NativeMenu menu, string title = "") + internal void Initialise(NativeMenu managedMenu, string title) { - var disposables = new CompositeDisposable(); - - if (ManagedMenu == null) - { - ManagedMenu = menu; - } - else if (ManagedMenu != menu) - { - ManagedMenu = menu; - } - - _exporter = exporter; + ManagedMenu = managedMenu; ((INotifyCollectionChanged)ManagedMenu.Items).CollectionChanged += OnMenuItemsChanged; - disposables.Add(Disposable.Create(() => ((INotifyCollectionChanged)ManagedMenu.Items).CollectionChanged -= OnMenuItemsChanged)); - if (!string.IsNullOrWhiteSpace(title)) { using (var buffer = new Utf8Buffer(title)) @@ -85,10 +69,29 @@ namespace Avalonia.Native.Interop Title = buffer.DangerousGetHandle(); } } + } + + internal void Deinitialise() + { + ((INotifyCollectionChanged)ManagedMenu.Items).CollectionChanged -= OnMenuItemsChanged; + + foreach(var item in _menuItems) + { + item.Deinitialise(); + item.Dispose(); + } + } + + internal void Update(IAvaloniaNativeFactory factory, NativeMenu menu) + { + if(menu != ManagedMenu) + { + throw new ArgumentException("The menu being updated does not match.", nameof(menu)); + } for (int i = 0; i < menu.Items.Count; i++) { - IAvnAppMenuItem nativeItem = null; + IAvnAppMenuItem nativeItem; if (i >= _menuItems.Count) { @@ -109,7 +112,7 @@ namespace Avalonia.Native.Interop if (menu.Items[i] is NativeMenuItem nmi) { - disposables.Add(nativeItem.Update(exporter, factory, nmi)); + nativeItem.Update(factory, nmi); } } @@ -117,13 +120,11 @@ namespace Avalonia.Native.Interop { RemoveAndDispose(_menuItems[_menuItems.Count - 1]); } - - return disposables; } private void OnMenuItemsChanged(object sender, NotifyCollectionChangedEventArgs e) { - _exporter.QueueReset(); + // update menu items. } } } diff --git a/src/Avalonia.Native/IAvnAppMenuItem.cs b/src/Avalonia.Native/IAvnAppMenuItem.cs index 66dde71a80..699d02caa5 100644 --- a/src/Avalonia.Native/IAvnAppMenuItem.cs +++ b/src/Avalonia.Native/IAvnAppMenuItem.cs @@ -7,8 +7,7 @@ namespace Avalonia.Native.Interop { public partial class IAvnAppMenuItem { - private IAvnAppMenu _subMenu; - private AvaloniaNativeMenuExporter _exporter; + private IAvnAppMenu _subMenu; private CompositeDisposable _propertyDisposables = new CompositeDisposable(); private IDisposable _currentActionDisposable; @@ -22,7 +21,7 @@ namespace Avalonia.Native.Interop } } - private void UpdateIsChecked (bool isChecked) + private void UpdateIsChecked(bool isChecked) { IsChecked = isChecked; } @@ -33,10 +32,10 @@ namespace Avalonia.Native.Interop using (var buffer = new Utf8Buffer(gesture == null ? "" : OsxUnicodeKeys.ConvertOSXSpecialKeyCodes(gesture.Key))) { SetGesture(buffer.DangerousGetHandle(), (AvnInputModifiers)gesture.KeyModifiers); - } + } } - private void UpdateAction (NativeMenuItem item) + private void UpdateAction(NativeMenuItem item) { _currentActionDisposable?.Dispose(); @@ -61,26 +60,40 @@ namespace Avalonia.Native.Interop SetAction(action, callback); } - internal void Initialise() + internal void Initialise(NativeMenuItemBase nativeMenuItem) { - _propertyDisposables.Add(Disposable.Create(() => ManagedMenuItem.GetObservable(NativeMenuItem.HeaderProperty) - .Subscribe(x => UpdateTitle(x)))); + ManagedMenuItem = nativeMenuItem; + + if (ManagedMenuItem is NativeMenuItem item) + { + UpdateTitle(item.Header); + + UpdateGesture(item.Gesture); + + UpdateAction(ManagedMenuItem as NativeMenuItem); - _propertyDisposables.Add(Disposable.Create(() => ManagedMenuItem.GetObservable(NativeMenuItem.GestureProperty) - .Subscribe(x => UpdateGesture(x)))); + UpdateIsChecked(item.IsChecked); - _propertyDisposables.Add(Disposable.Create(() => ManagedMenuItem.GetObservable(NativeMenuItem.CommandProperty) - .Subscribe(x => UpdateAction(ManagedMenuItem as NativeMenuItem)))); + _propertyDisposables.Add(Disposable.Create(() => ManagedMenuItem.GetObservable(NativeMenuItem.HeaderProperty) + .Subscribe(x => UpdateTitle(x)))); - _propertyDisposables.Add(Disposable.Create(() => ManagedMenuItem.GetObservable(NativeMenuItem.IsCheckedProperty) - .Subscribe(x => UpdateIsChecked(x)))); + _propertyDisposables.Add(Disposable.Create(() => ManagedMenuItem.GetObservable(NativeMenuItem.GestureProperty) + .Subscribe(x => UpdateGesture(x)))); + + _propertyDisposables.Add(Disposable.Create(() => ManagedMenuItem.GetObservable(NativeMenuItem.CommandProperty) + .Subscribe(x => UpdateAction(ManagedMenuItem as NativeMenuItem)))); + + _propertyDisposables.Add(Disposable.Create(() => ManagedMenuItem.GetObservable(NativeMenuItem.IsCheckedProperty) + .Subscribe(x => UpdateIsChecked(x)))); + } } - internal void Deinitialise () + internal void Deinitialise() { - if(_subMenu != null) + if (_subMenu != null) { - _subMenu.Update(null, null, null); + _subMenu.Deinitialise(); + _subMenu.Dispose(); _subMenu = null; } @@ -88,42 +101,34 @@ namespace Avalonia.Native.Interop _currentActionDisposable?.Dispose(); } - internal IDisposable Update(AvaloniaNativeMenuExporter exporter, IAvaloniaNativeFactory factory, NativeMenuItem item) - { - var disposables = new CompositeDisposable(); - - _exporter = exporter; - - ManagedMenuItem = item; - - UpdateTitle(item.Header); - - UpdateGesture(item.Gesture); - - UpdateAction(ManagedMenuItem as NativeMenuItem); - - UpdateIsChecked(item.IsChecked); + internal void Update(IAvaloniaNativeFactory factory, NativeMenuItem item) + { + if(item != ManagedMenuItem) + { + throw new ArgumentException("The item does not match the menuitem being updated.", nameof(item)); + } if (item.Menu != null) { if (_subMenu == null) { _subMenu = factory.CreateMenu(); + + _subMenu.Initialise(item.Menu, item.Header); } SetSubMenu(_subMenu); - disposables.Add(_subMenu.Update(exporter, factory, item.Menu, item.Header)); + _subMenu.Update(factory, item.Menu); } if (item.Menu == null && _subMenu != null) { - // todo remove submenu. + _subMenu.Deinitialise(); + _subMenu.Dispose(); - // needs implementing on native side also. + SetSubMenu(null); } - - return disposables; } } }