Browse Source

Fix events for ContextMenu and Popup.

Dariusz Komosinski 6 years ago
parent
commit
3c787b3d4d

+ 32 - 6
src/Avalonia.Controls/ContextMenu.cs

@@ -7,6 +7,7 @@ using Avalonia.Controls.Platform;
 using Avalonia.Controls.Primitives;
 using Avalonia.Controls.Templates;
 using Avalonia.Input;
+using Avalonia.Interactivity;
 using Avalonia.LogicalTree;
 
 namespace Avalonia.Controls
@@ -90,9 +91,14 @@ namespace Avalonia.Controls
         /// <param name="control">The control.</param>
         public void Open(Control control)
         {
+            if (IsOpen)
+            {
+                return;
+            }
+
             if (_popup == null)
             {
-                _popup = new Popup()
+                _popup = new Popup
                 {
                     PlacementMode = PlacementMode.Pointer,
                     PlacementTarget = control,
@@ -107,7 +113,14 @@ namespace Avalonia.Controls
             ((ISetLogicalParent)_popup).SetParent(control);
             _popup.Child = this;
             _popup.IsOpen = true;
+
             IsOpen = true;
+
+            RaiseEvent(new RoutedEventArgs
+            {
+                RoutedEvent = MenuOpenedEvent,
+                Source = this,
+            });
         }
 
         /// <summary>
@@ -115,13 +128,15 @@ namespace Avalonia.Controls
         /// </summary>
         public override void Close()
         {
+            if (!IsOpen)
+            {
+                return;
+            }
+
             if (_popup != null && _popup.IsVisible)
             {
                 _popup.IsOpen = false;
             }
-
-            SelectedIndex = -1;
-            IsOpen = false;
         }
 
         protected override IItemContainerGenerator CreateItemContainerGenerator()
@@ -129,6 +144,18 @@ namespace Avalonia.Controls
             return new MenuItemContainerGenerator(this);
         }
 
+        private void CloseCore()
+        {
+            SelectedIndex = -1;
+            IsOpen = false;
+
+            RaiseEvent(new RoutedEventArgs
+            {
+                RoutedEvent = MenuClosedEvent,
+                Source = this,
+            });
+        }
+
         private void PopupOpened(object sender, EventArgs e)
         {
             Focus();
@@ -145,8 +172,7 @@ namespace Avalonia.Controls
                     i.IsSubMenuOpen = false;
                 }
 
-                contextMenu.IsOpen = false;
-                contextMenu.SelectedIndex = -1;
+                contextMenu.CloseCore();
             }
         }
 

+ 25 - 21
src/Avalonia.Controls/Menu.cs

@@ -40,37 +40,41 @@ namespace Avalonia.Controls
         /// <inheritdoc/>
         public override void Close()
         {
-            if (IsOpen)
+            if (!IsOpen)
             {
-                foreach (var i in ((IMenu)this).SubItems)
-                {
-                    i.Close();
-                }
-
-                IsOpen = false;
-                SelectedIndex = -1;
+                return;
+            }
 
-                RaiseEvent(new RoutedEventArgs
-                {
-                    RoutedEvent = MenuClosedEvent,
-                    Source = this,
-                });
+            foreach (var i in ((IMenu)this).SubItems)
+            {
+                i.Close();
             }
+
+            IsOpen = false;
+            SelectedIndex = -1;
+
+            RaiseEvent(new RoutedEventArgs
+            {
+                RoutedEvent = MenuClosedEvent,
+                Source = this,
+            });
         }
 
         /// <inheritdoc/>
         public override void Open()
         {
-            if (!IsOpen)
+            if (IsOpen)
             {
-                IsOpen = true;
-
-                RaiseEvent(new RoutedEventArgs
-                {
-                    RoutedEvent = MenuOpenedEvent,
-                    Source = this,
-                });
+                return;
             }
+
+            IsOpen = true;
+
+            RaiseEvent(new RoutedEventArgs
+            {
+                RoutedEvent = MenuOpenedEvent,
+                Source = this,
+            });
         }
 
         /// <inheritdoc/>

+ 2 - 2
src/Avalonia.Controls/MenuBase.cs

@@ -31,13 +31,13 @@ namespace Avalonia.Controls
         /// Defines the <see cref="MenuOpened"/> event.
         /// </summary>
         public static readonly RoutedEvent<RoutedEventArgs> MenuOpenedEvent =
-            RoutedEvent.Register<MenuItem, RoutedEventArgs>(nameof(MenuOpened), RoutingStrategies.Bubble);
+            RoutedEvent.Register<MenuBase, RoutedEventArgs>(nameof(MenuOpened), RoutingStrategies.Bubble);
 
         /// <summary>
         /// Defines the <see cref="MenuClosed"/> event.
         /// </summary>
         public static readonly RoutedEvent<RoutedEventArgs> MenuClosedEvent =
-            RoutedEvent.Register<MenuItem, RoutedEventArgs>(nameof(MenuClosed), RoutingStrategies.Bubble);
+            RoutedEvent.Register<MenuBase, RoutedEventArgs>(nameof(MenuClosed), RoutingStrategies.Bubble);
 
         private bool _isOpen;
 

+ 31 - 4
src/Avalonia.Controls/Primitives/Popup.cs

@@ -2,6 +2,7 @@
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
 using System;
+using System.Diagnostics;
 using System.Linq;
 using Avalonia.Input;
 using Avalonia.Input.Raw;
@@ -270,9 +271,10 @@ namespace Avalonia.Controls.Primitives
                 _popupRoot.SnapInsideScreenEdges();
             }
 
-            _ignoreIsOpenChanged = true;
-            IsOpen = true;
-            _ignoreIsOpenChanged = false;
+            using (BeginIgnoringIsOpen())
+            {
+                IsOpen = true;
+            }
 
             Opened?.Invoke(this, EventArgs.Empty);
         }
@@ -305,7 +307,11 @@ namespace Avalonia.Controls.Primitives
                 _popupRoot.Hide();
             }
 
-            IsOpen = false;
+            using (BeginIgnoringIsOpen())
+            {
+                IsOpen = false;
+            }
+
             Closed?.Invoke(this, EventArgs.Empty);
         }
 
@@ -467,5 +473,26 @@ namespace Avalonia.Controls.Primitives
                 Close();
             }
         }
+
+        private IgnoreIsOpenScope BeginIgnoringIsOpen()
+        {
+            return new IgnoreIsOpenScope(this);
+        }
+
+        private readonly struct IgnoreIsOpenScope : IDisposable
+        {
+            private readonly Popup _owner;
+
+            public IgnoreIsOpenScope(Popup owner)
+            {
+                _owner = owner;
+                _owner._ignoreIsOpenChanged = true;
+            }
+
+            public void Dispose()
+            {
+                _owner._ignoreIsOpenChanged = false;
+            }
+        }
     }
 }

+ 54 - 0
tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs

@@ -16,6 +16,60 @@ namespace Avalonia.Controls.UnitTests
         private Mock<IPopupImpl> popupImpl;
         private MouseTestHelper _mouse = new MouseTestHelper();
 
+        [Fact]
+        public void Opening_Raises_Single_Opened_Event()
+        {
+            using (Application())
+            {
+                var sut = new ContextMenu();
+                var target = new Panel
+                {
+                    ContextMenu = sut
+                };
+
+                new Window { Content = target };
+
+                int openedCount = 0;
+
+                sut.MenuOpened += (sender, args) =>
+                {
+                    openedCount++;
+                };
+
+                sut.Open(null);
+
+                Assert.Equal(1, openedCount);
+            }
+        }
+
+        [Fact]
+        public void Closing_Raises_Single_Closed_Event()
+        {
+            using (Application())
+            {
+                var sut = new ContextMenu();
+                var target = new Panel
+                {
+                    ContextMenu = sut
+                };
+
+                new Window { Content = target };
+
+                sut.Open(null);
+
+                int closedCount = 0;
+
+                sut.MenuClosed += (sender, args) =>
+                {
+                    closedCount++;
+                };
+
+                sut.Close();
+
+                Assert.Equal(1, closedCount);
+            }
+        }
+
         [Fact]
         public void Clicking_On_Control_Toggles_ContextMenu()
         {

+ 48 - 0
tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs

@@ -3,6 +3,7 @@
 
 using System;
 using System.Collections.Specialized;
+using System.Diagnostics;
 using System.Linq;
 using Moq;
 using Avalonia.Controls.Presenters;
@@ -185,6 +186,53 @@ namespace Avalonia.Controls.UnitTests.Primitives
             }
         }
 
+        [Fact]
+        public void Popup_Open_Should_Raise_Single_Opened_Event()
+        {
+            using (CreateServices())
+            {
+                var window = new Window();
+                var target = new Popup();
+
+                window.Content = target;
+
+                int openedCount = 0;
+
+                target.Opened += (sender, args) =>
+                {
+                    openedCount++;
+                };
+
+                target.Open();
+
+                Assert.Equal(1, openedCount);
+            }
+        }
+
+        [Fact]
+        public void Popup_Close_Should_Raise_Single_Closed_Event()
+        {
+            using (CreateServices())
+            {
+                var window = new Window();
+                var target = new Popup();
+
+                window.Content = target;
+                target.Open();
+
+                int closedCount = 0;
+
+                target.Closed += (sender, args) =>
+                {
+                    closedCount++;
+                };
+
+                target.Close();
+
+                Assert.Equal(1, closedCount);
+            }
+        }
+
         [Fact]
         public void PopupRoot_Should_Have_Template_Applied()
         {