Browse Source

dont return disposables, dispose as items are removed.

pull/3762/head
Dan Walmsley 6 years ago
parent
commit
2f13cbff5f
  1. 17
      src/Avalonia.Native/AvaloniaNativeMenuExporter.cs
  2. 51
      src/Avalonia.Native/IAvnAppMenu.cs
  3. 79
      src/Avalonia.Native/IAvnAppMenuItem.cs

17
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);
}

51
src/Avalonia.Native/IAvnAppMenu.cs

@ -9,9 +9,9 @@ namespace Avalonia.Native.Interop
{
public partial class IAvnAppMenu
{
private AvaloniaNativeMenuExporter _exporter;
private List<IAvnAppMenuItem> _menuItems = new List<IAvnAppMenuItem>();
private Dictionary<NativeMenuItemBase, IAvnAppMenuItem> _menuItemLookup = new Dictionary<NativeMenuItemBase, IAvnAppMenuItem>();
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.
}
}
}

79
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;
}
}
}

Loading…
Cancel
Save