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 bool _exported = false;
private IAvnWindow _nativeWindow; private IAvnWindow _nativeWindow;
private NativeMenu _menu; private NativeMenu _menu;
private IAvnAppMenu _nativeMenu; private IAvnAppMenu _nativeMenu;
private IDisposable _disposable;
public AvaloniaNativeMenuExporter(IAvnWindow nativeWindow, IAvaloniaNativeFactory factory) public AvaloniaNativeMenuExporter(IAvnWindow nativeWindow, IAvaloniaNativeFactory factory)
{ {
@ -109,6 +108,8 @@ namespace Avalonia.Native
if (_nativeMenu is null) if (_nativeMenu is null)
{ {
_nativeMenu = _factory.CreateMenu(); _nativeMenu = _factory.CreateMenu();
_nativeMenu.Initialise(menu, "");
} }
} }
@ -131,9 +132,8 @@ namespace Avalonia.Native
menuItem.Menu = menu; menuItem.Menu = menu;
var setMenu = _nativeMenu.ManagedMenu != appMenuHolder; var setMenu = _nativeMenu.ManagedMenu != appMenuHolder;
_disposable?.Dispose(); _nativeMenu.Update(_factory, appMenuHolder);
_disposable = _nativeMenu.Update(this, _factory, appMenuHolder);
if (setMenu) if (setMenu)
{ {
@ -150,11 +150,12 @@ namespace Avalonia.Native
if (_nativeMenu is null) if (_nativeMenu is null)
{ {
_nativeMenu = _factory.CreateMenu(); _nativeMenu = _factory.CreateMenu();
_nativeMenu.Initialise(menu, "");
} }
} }
_disposable?.Dispose(); _nativeMenu.Update(_factory, menu);
_disposable = _nativeMenu.Update(this, _factory, menu);
avnWindow.SetMainMenu(_nativeMenu); avnWindow.SetMainMenu(_nativeMenu);
} }

51
src/Avalonia.Native/IAvnAppMenu.cs

@ -9,9 +9,9 @@ namespace Avalonia.Native.Interop
{ {
public partial class IAvnAppMenu public partial class IAvnAppMenu
{ {
private AvaloniaNativeMenuExporter _exporter;
private List<IAvnAppMenuItem> _menuItems = new List<IAvnAppMenuItem>(); private List<IAvnAppMenuItem> _menuItems = new List<IAvnAppMenuItem>();
private Dictionary<NativeMenuItemBase, IAvnAppMenuItem> _menuItemLookup = new Dictionary<NativeMenuItemBase, IAvnAppMenuItem>(); private Dictionary<NativeMenuItemBase, IAvnAppMenuItem> _menuItemLookup = new Dictionary<NativeMenuItemBase, IAvnAppMenuItem>();
private CompositeDisposable _propertyDisposables = new CompositeDisposable();
internal NativeMenu ManagedMenu { get; private set; } internal NativeMenu ManagedMenu { get; private set; }
@ -19,12 +19,9 @@ namespace Avalonia.Native.Interop
{ {
_menuItemLookup.Remove(item.ManagedMenuItem); _menuItemLookup.Remove(item.ManagedMenuItem);
_menuItems.Remove(item); _menuItems.Remove(item);
item.Update(null, null, null);
RemoveItem(item); RemoveItem(item);
item.Deinitialise(); item.Deinitialise();
item.Dispose(); item.Dispose();
} }
@ -41,7 +38,7 @@ namespace Avalonia.Native.Interop
{ {
var result = CreateNew(factory, item); var result = CreateNew(factory, item);
result.Initialise(); result.Initialise(item);
_menuItemLookup.Add(result.ManagedMenuItem, result); _menuItemLookup.Add(result.ManagedMenuItem, result);
_menuItems.Insert(index, result); _menuItems.Insert(index, result);
@ -59,25 +56,12 @@ namespace Avalonia.Native.Interop
return nativeItem; return nativeItem;
} }
internal IDisposable Update(AvaloniaNativeMenuExporter exporter, IAvaloniaNativeFactory factory, NativeMenu menu, string title = "") internal void Initialise(NativeMenu managedMenu, string title)
{ {
var disposables = new CompositeDisposable(); ManagedMenu = managedMenu;
if (ManagedMenu == null)
{
ManagedMenu = menu;
}
else if (ManagedMenu != menu)
{
ManagedMenu = menu;
}
_exporter = exporter;
((INotifyCollectionChanged)ManagedMenu.Items).CollectionChanged += OnMenuItemsChanged; ((INotifyCollectionChanged)ManagedMenu.Items).CollectionChanged += OnMenuItemsChanged;
disposables.Add(Disposable.Create(() => ((INotifyCollectionChanged)ManagedMenu.Items).CollectionChanged -= OnMenuItemsChanged));
if (!string.IsNullOrWhiteSpace(title)) if (!string.IsNullOrWhiteSpace(title))
{ {
using (var buffer = new Utf8Buffer(title)) using (var buffer = new Utf8Buffer(title))
@ -85,10 +69,29 @@ namespace Avalonia.Native.Interop
Title = buffer.DangerousGetHandle(); 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++) for (int i = 0; i < menu.Items.Count; i++)
{ {
IAvnAppMenuItem nativeItem = null; IAvnAppMenuItem nativeItem;
if (i >= _menuItems.Count) if (i >= _menuItems.Count)
{ {
@ -109,7 +112,7 @@ namespace Avalonia.Native.Interop
if (menu.Items[i] is NativeMenuItem nmi) 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]); RemoveAndDispose(_menuItems[_menuItems.Count - 1]);
} }
return disposables;
} }
private void OnMenuItemsChanged(object sender, NotifyCollectionChangedEventArgs e) 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 public partial class IAvnAppMenuItem
{ {
private IAvnAppMenu _subMenu; private IAvnAppMenu _subMenu;
private AvaloniaNativeMenuExporter _exporter;
private CompositeDisposable _propertyDisposables = new CompositeDisposable(); private CompositeDisposable _propertyDisposables = new CompositeDisposable();
private IDisposable _currentActionDisposable; private IDisposable _currentActionDisposable;
@ -22,7 +21,7 @@ namespace Avalonia.Native.Interop
} }
} }
private void UpdateIsChecked (bool isChecked) private void UpdateIsChecked(bool isChecked)
{ {
IsChecked = isChecked; IsChecked = isChecked;
} }
@ -33,10 +32,10 @@ namespace Avalonia.Native.Interop
using (var buffer = new Utf8Buffer(gesture == null ? "" : OsxUnicodeKeys.ConvertOSXSpecialKeyCodes(gesture.Key))) using (var buffer = new Utf8Buffer(gesture == null ? "" : OsxUnicodeKeys.ConvertOSXSpecialKeyCodes(gesture.Key)))
{ {
SetGesture(buffer.DangerousGetHandle(), (AvnInputModifiers)gesture.KeyModifiers); SetGesture(buffer.DangerousGetHandle(), (AvnInputModifiers)gesture.KeyModifiers);
} }
} }
private void UpdateAction (NativeMenuItem item) private void UpdateAction(NativeMenuItem item)
{ {
_currentActionDisposable?.Dispose(); _currentActionDisposable?.Dispose();
@ -61,26 +60,40 @@ namespace Avalonia.Native.Interop
SetAction(action, callback); SetAction(action, callback);
} }
internal void Initialise() internal void Initialise(NativeMenuItemBase nativeMenuItem)
{ {
_propertyDisposables.Add(Disposable.Create(() => ManagedMenuItem.GetObservable(NativeMenuItem.HeaderProperty) ManagedMenuItem = nativeMenuItem;
.Subscribe(x => UpdateTitle(x))));
if (ManagedMenuItem is NativeMenuItem item)
{
UpdateTitle(item.Header);
UpdateGesture(item.Gesture);
UpdateAction(ManagedMenuItem as NativeMenuItem);
_propertyDisposables.Add(Disposable.Create(() => ManagedMenuItem.GetObservable(NativeMenuItem.GestureProperty) UpdateIsChecked(item.IsChecked);
.Subscribe(x => UpdateGesture(x))));
_propertyDisposables.Add(Disposable.Create(() => ManagedMenuItem.GetObservable(NativeMenuItem.CommandProperty) _propertyDisposables.Add(Disposable.Create(() => ManagedMenuItem.GetObservable(NativeMenuItem.HeaderProperty)
.Subscribe(x => UpdateAction(ManagedMenuItem as NativeMenuItem)))); .Subscribe(x => UpdateTitle(x))));
_propertyDisposables.Add(Disposable.Create(() => ManagedMenuItem.GetObservable(NativeMenuItem.IsCheckedProperty) _propertyDisposables.Add(Disposable.Create(() => ManagedMenuItem.GetObservable(NativeMenuItem.GestureProperty)
.Subscribe(x => UpdateIsChecked(x)))); .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; _subMenu = null;
} }
@ -88,42 +101,34 @@ namespace Avalonia.Native.Interop
_currentActionDisposable?.Dispose(); _currentActionDisposable?.Dispose();
} }
internal IDisposable Update(AvaloniaNativeMenuExporter exporter, IAvaloniaNativeFactory factory, NativeMenuItem item) internal void Update(IAvaloniaNativeFactory factory, NativeMenuItem item)
{ {
var disposables = new CompositeDisposable(); if(item != ManagedMenuItem)
{
_exporter = exporter; throw new ArgumentException("The item does not match the menuitem being updated.", nameof(item));
}
ManagedMenuItem = item;
UpdateTitle(item.Header);
UpdateGesture(item.Gesture);
UpdateAction(ManagedMenuItem as NativeMenuItem);
UpdateIsChecked(item.IsChecked);
if (item.Menu != null) if (item.Menu != null)
{ {
if (_subMenu == null) if (_subMenu == null)
{ {
_subMenu = factory.CreateMenu(); _subMenu = factory.CreateMenu();
_subMenu.Initialise(item.Menu, item.Header);
} }
SetSubMenu(_subMenu); SetSubMenu(_subMenu);
disposables.Add(_subMenu.Update(exporter, factory, item.Menu, item.Header)); _subMenu.Update(factory, item.Menu);
} }
if (item.Menu == null && _subMenu != null) 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