Browse Source

Implemented cancelling ContextMenu opening/closing. (#1781)

pull/1804/head
WojciechKrysiak 8 years ago
committed by Nelson Carrillo
parent
commit
ec57b63908
  1. 71
      src/Avalonia.Controls/ContextMenu.cs
  2. 179
      tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs

71
src/Avalonia.Controls/ContextMenu.cs

@ -7,11 +7,20 @@ namespace Avalonia.Controls
using System;
using System.Reactive.Linq;
using System.Linq;
using System.ComponentModel;
public class ContextMenu : SelectingItemsControl
{
private bool _isOpen;
private Popup _popup;
/// <summary>
/// Defines the <see cref="IsOpen"/> property.
/// </summary>
public static readonly DirectProperty<ContextMenu, bool> IsOpenProperty =
AvaloniaProperty.RegisterDirect<ContextMenu, bool>(nameof(IsOpen), o => o.IsOpen);
/// <summary>
/// Initializes static members of the <see cref="ContextMenu"/> class.
/// </summary>
@ -22,6 +31,26 @@ namespace Avalonia.Controls
MenuItem.ClickEvent.AddClassHandler<ContextMenu>(x => x.OnContextMenuClick, handledEventsToo: true);
}
/// <summary>
/// Gets a value indicating whether the popup is open
/// </summary>
public bool IsOpen => _isOpen;
/// <summary>
/// Occurs when the value of the
/// <see cref="P:Avalonia.Controls.ContextMenu.IsOpen" />
/// property is changing from false to true.
/// </summary>
public event CancelEventHandler ContextMenuOpening;
/// <summary>
/// Occurs when the value of the
/// <see cref="P:Avalonia.Controls.ContextMenu.IsOpen" />
/// property is changing from true to false.
/// </summary>
public event CancelEventHandler ContextMenuClosing;
/// <summary>
/// Called when the <see cref="Control.ContextMenu"/> property changes on a control.
/// </summary>
@ -59,12 +88,12 @@ namespace Avalonia.Controls
{
if (_popup != null && _popup.IsVisible)
{
_popup.Close();
_popup.IsOpen = false;
}
SelectedIndex = -1;
_isOpen = false;
SetAndRaise(IsOpenProperty, ref _isOpen, false);
}
/// <summary>
@ -89,11 +118,11 @@ namespace Avalonia.Controls
}
((ISetLogicalParent)_popup).SetParent(control);
_popup.Child = control.ContextMenu;
_popup.Child = this;
_popup.Open();
_popup.IsOpen = true;
control.ContextMenu._isOpen = true;
SetAndRaise(IsOpenProperty, ref _isOpen, true);
}
}
@ -118,21 +147,37 @@ namespace Avalonia.Controls
var control = (Control)sender;
var contextMenu = control.ContextMenu;
if (e.MouseButton == MouseButton.Right)
if (control.ContextMenu._isOpen)
{
if (control.ContextMenu._isOpen)
{
control.ContextMenu.Hide();
}
if (contextMenu.CancelClosing())
return;
contextMenu.Show(control);
control.ContextMenu.Hide();
e.Handled = true;
}
else if (contextMenu._isOpen)
if (e.MouseButton == MouseButton.Right)
{
control.ContextMenu.Hide();
if (contextMenu.CancelOpening())
return;
contextMenu.Show(control);
e.Handled = true;
}
}
private bool CancelClosing()
{
var eventArgs = new CancelEventArgs();
ContextMenuClosing?.Invoke(this, eventArgs);
return eventArgs.Cancel;
}
private bool CancelOpening()
{
var eventArgs = new CancelEventArgs();
ContextMenuOpening?.Invoke(this, eventArgs);
return eventArgs.Cancel;
}
}
}

179
tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs

@ -0,0 +1,179 @@
using System;
using System.Windows.Input;
using Avalonia.Controls.Primitives;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Markup.Data;
using Avalonia.Platform;
using Avalonia.UnitTests;
using Moq;
using Xunit;
namespace Avalonia.Controls.UnitTests
{
public class ContextMenuTests
{
private Mock<IPopupImpl> popupImpl;
[Fact]
public void Clicking_On_Control_Toggles_ContextMenu()
{
using (Application())
{
popupImpl.Setup(x => x.Show()).Verifiable();
popupImpl.Setup(x => x.Hide()).Verifiable();
var sut = new ContextMenu();
var target = new Panel
{
ContextMenu = sut
};
new Window { Content = target };
target.RaiseEvent(new PointerReleasedEventArgs
{
RoutedEvent = InputElement.PointerReleasedEvent,
MouseButton = MouseButton.Right
});
Assert.True(sut.IsOpen);
target.RaiseEvent(new PointerReleasedEventArgs
{
RoutedEvent = InputElement.PointerReleasedEvent,
MouseButton = MouseButton.None
});
Assert.False(sut.IsOpen);
popupImpl.Verify(x => x.Show(), Times.Once);
popupImpl.Verify(x => x.Hide(), Times.Once);
}
}
[Fact]
public void Right_Clicking_On_Control_Twice_Re_Opens_ContextMenu()
{
using (Application())
{
popupImpl.Setup(x => x.Show()).Verifiable();
popupImpl.Setup(x => x.Hide()).Verifiable();
var sut = new ContextMenu();
var target = new Panel
{
ContextMenu = sut
};
new Window { Content = target };
target.RaiseEvent(new PointerReleasedEventArgs
{
RoutedEvent = InputElement.PointerReleasedEvent,
MouseButton = MouseButton.Right
});
Assert.True(sut.IsOpen);
target.RaiseEvent(new PointerReleasedEventArgs
{
RoutedEvent = InputElement.PointerReleasedEvent,
MouseButton = MouseButton.Right
});
Assert.True(sut.IsOpen);
popupImpl.Verify(x => x.Hide(), Times.Once);
popupImpl.Verify(x => x.Show(), Times.Exactly(2));
}
}
[Fact]
public void Cancelling_Opening_Does_Not_Show_ContextMenu()
{
using (Application())
{
popupImpl.Setup(x => x.Show()).Verifiable();
bool eventCalled = false;
var sut = new ContextMenu();
var target = new Panel
{
ContextMenu = sut
};
new Window { Content = target };
sut.ContextMenuOpening += (c, e) => { eventCalled = true; e.Cancel = true; };
target.RaiseEvent(new PointerReleasedEventArgs
{
RoutedEvent = InputElement.PointerReleasedEvent,
MouseButton = MouseButton.Right
});
Assert.True(eventCalled);
Assert.False(sut.IsOpen);
popupImpl.Verify(x => x.Show(), Times.Never);
}
}
[Fact]
public void Cancelling_Closing_Leaves_ContextMenuOpen()
{
using (Application())
{
popupImpl.Setup(x => x.Show()).Verifiable();
popupImpl.Setup(x => x.Hide()).Verifiable();
bool eventCalled = false;
var sut = new ContextMenu();
var target = new Panel
{
ContextMenu = sut
};
new Window { Content = target };
sut.ContextMenuClosing += (c, e) => { eventCalled = true; e.Cancel = true; };
target.RaiseEvent(new PointerReleasedEventArgs
{
RoutedEvent = InputElement.PointerReleasedEvent,
MouseButton = MouseButton.Right
});
Assert.True(sut.IsOpen);
target.RaiseEvent(new PointerReleasedEventArgs
{
RoutedEvent = InputElement.PointerReleasedEvent,
MouseButton = MouseButton.None
});
Assert.True(eventCalled);
Assert.True(sut.IsOpen);
popupImpl.Verify(x => x.Show(), Times.Once());
popupImpl.Verify(x => x.Hide(), Times.Never);
}
}
private IDisposable Application()
{
var screen = new Rect(new Point(), new Size(100, 100));
var screenImpl = new Mock<IScreenImpl>();
screenImpl.Setup(x => x.ScreenCount).Returns(1);
screenImpl.Setup(X => X.AllScreens).Returns( new[] { new Screen(screen, screen, true) });
var windowImpl = new Mock<IWindowImpl>();
windowImpl.Setup(x => x.Screen).Returns(screenImpl.Object);
popupImpl = new Mock<IPopupImpl>();
popupImpl.SetupGet(x => x.Scaling).Returns(1);
var services = TestServices.StyledWindow.With(
inputManager: new InputManager(),
windowImpl: windowImpl.Object,
windowingPlatform: new MockWindowingPlatform(() => windowImpl.Object, () => popupImpl.Object));
return UnitTestApplication.Start(services);
}
}
}
Loading…
Cancel
Save