diff --git a/tests/Avalonia.Controls.UnitTests/FlyoutTests.cs b/tests/Avalonia.Controls.UnitTests/FlyoutTests.cs
new file mode 100644
index 0000000000..0dce73180e
--- /dev/null
+++ b/tests/Avalonia.Controls.UnitTests/FlyoutTests.cs
@@ -0,0 +1,325 @@
+using System;
+using System.Linq;
+using Avalonia.Input;
+using Avalonia.Markup.Xaml;
+using Avalonia.Media;
+using Avalonia.UnitTests;
+using Avalonia.VisualTree;
+using Xunit;
+
+namespace Avalonia.Controls.UnitTests
+{
+ public class FlyoutTests
+ {
+ [Fact]
+ public void Opening_Raises_Single_Opening_Event()
+ {
+ using (CreateServicesWithFocus())
+ {
+ var window = PreparedWindow();
+ window.Show();
+
+ int tracker = 0;
+ Flyout f = new Flyout();
+ f.Opening += (s, e) =>
+ {
+ tracker++;
+ };
+ f.ShowAt(window);
+
+ Assert.Equal(1, tracker);
+ }
+ }
+
+ [Fact]
+ public void Opening_Raises_Single_Opened_Event()
+ {
+ using (CreateServicesWithFocus())
+ {
+ var window = PreparedWindow();
+ window.Show();
+
+ int tracker = 0;
+ Flyout f = new Flyout();
+ f.Opened += (s, e) =>
+ {
+ tracker++;
+ };
+ f.ShowAt(window);
+
+ Assert.Equal(1, tracker);
+ }
+ }
+
+ [Fact]
+ public void Closing_Raises_Single_Closing_Event()
+ {
+ using (CreateServicesWithFocus())
+ {
+ var window = PreparedWindow();
+ window.Show();
+
+ int tracker = 0;
+ Flyout f = new Flyout();
+ f.Closing += (s, e) =>
+ {
+ tracker++;
+ };
+ f.ShowAt(window);
+ f.Hide();
+
+ Assert.Equal(1, tracker);
+ }
+ }
+
+ [Fact]
+ public void Closing_Raises_Single_Closed_Event()
+ {
+ using (CreateServicesWithFocus())
+ {
+ var window = PreparedWindow();
+ window.Show();
+
+ int tracker = 0;
+ Flyout f = new Flyout();
+ f.Closed += (s, e) =>
+ {
+ tracker++;
+ };
+ f.ShowAt(window);
+ f.Hide();
+
+ Assert.Equal(1, tracker);
+ }
+ }
+
+ [Fact]
+ public void Cancel_Closing_Keeps_Flyout_Open()
+ {
+ using (CreateServicesWithFocus())
+ {
+ var window = PreparedWindow();
+ window.Show();
+
+ int tracker = 0;
+ Flyout f = new Flyout();
+ f.Closing += (s, e) =>
+ {
+ e.Cancel = true;
+ };
+ f.ShowAt(window);
+ f.Hide();
+
+ Assert.True(f.IsOpen);
+ }
+ }
+
+ [Fact]
+ public void Flyout_Has_Uncancellable_Close_Before_Showing_On_A_Different_Target()
+ {
+ using (CreateServicesWithFocus())
+ {
+ var window = PreparedWindow();
+ Button target1 = new Button();
+ Button target2 = new Button();
+
+ window.Content = new StackPanel
+ {
+ Children =
+ {
+ target1,
+ target2
+ }
+ };
+ window.Show();
+
+ bool closingFired = false;
+ bool closedFired = false;
+ Flyout f = new Flyout();
+ f.Closing += (s, e) =>
+ {
+ closingFired = true; //This shouldn't happen
+ };
+ f.Closed += (s, e) =>
+ {
+ closedFired = true;
+ };
+
+ f.ShowAt(target1);
+
+ f.ShowAt(target2);
+
+ Assert.False(closingFired);
+ Assert.True(closedFired);
+ }
+ }
+
+ [Fact]
+ public void ShowMode_Standard_Attemps_Focus_Flyout_Content()
+ {
+ using (CreateServicesWithFocus())
+ {
+ var window = PreparedWindow();
+
+ var flyoutTextBox = new TextBox();
+ var button = new Button
+ {
+ Flyout = new Flyout
+ {
+ ShowMode = FlyoutShowMode.Standard,
+ Content = new Panel
+ {
+ Children =
+ {
+ flyoutTextBox
+ }
+ }
+ }
+ };
+
+ window.Content = button;
+ window.Show();
+
+ button.Focus();
+ Assert.True(FocusManager.Instance?.Current == button);
+ button.Flyout.ShowAt(button);
+ Assert.False(button.IsFocused);
+ Assert.True(FocusManager.Instance?.Current == flyoutTextBox);
+ }
+ }
+
+ [Fact]
+ public void ShowMode_Transient_Does_Not_Move_Focus_From_Target()
+ {
+ using (CreateServicesWithFocus())
+ {
+ var window = PreparedWindow();
+
+ var flyoutTextBox = new TextBox();
+ var button = new Button
+ {
+ Flyout = new Flyout
+ {
+ ShowMode = FlyoutShowMode.Transient,
+ Content = new Panel
+ {
+ Children =
+ {
+ flyoutTextBox
+ }
+ }
+ },
+ Content = "Test"
+ };
+
+ window.Content = button;
+ window.Show();
+
+ FocusManager.Instance?.Focus(button);
+ Assert.True(FocusManager.Instance?.Current == button);
+ button.Flyout.ShowAt(button);
+ Assert.True(FocusManager.Instance?.Current == button);
+ }
+ }
+
+ [Fact]
+ public void ContextFlyout_Can_Be_Set_In_Styles()
+ {
+ using (CreateServicesWithFocus())
+ {
+ var xaml = @"
+
+
+
+
+
+
+
+
+
+";
+
+ var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
+ var target1 = window.Find("target1");
+ var target2 = window.Find("target2");
+ var mouse = new MouseTestHelper();
+
+ Assert.NotNull(target1.ContextFlyout);
+ Assert.NotNull(target2.ContextFlyout);
+ Assert.Same(target1.ContextFlyout, target2.ContextFlyout);
+
+ window.Show();
+
+ var menu = target1.ContextFlyout;
+ mouse.Click(target1, MouseButton.Right);
+ Assert.True(menu.IsOpen);
+ mouse.Click(target2, MouseButton.Right);
+ Assert.True(menu.IsOpen);
+ }
+ }
+
+ [Fact]
+ public void Setting_FlyoutPresenterClasses_Sets_Classes_On_FlyoutPresenter()
+ {
+ using (CreateServicesWithFocus())
+ {
+ var xaml = @"
+
+
+
+
+";
+
+ var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
+ var flyoutPanel = new Panel();
+ var button = new Button
+ {
+ Content = "Test",
+ Flyout = new Flyout
+ {
+ Content = flyoutPanel
+ }
+ };
+ window.Content = button;
+ window.Show();
+
+ (button.Flyout as Flyout).FlyoutPresenterClasses.Add("TestClass");
+
+ button.Flyout.ShowAt(button);
+
+ var presenter = flyoutPanel.GetVisualAncestors().OfType().FirstOrDefault();
+ Assert.NotNull(presenter);
+ Assert.True((presenter.Background as ISolidColorBrush).Color == Colors.Red);
+ }
+ }
+
+ private IDisposable CreateServicesWithFocus()
+ {
+ return UnitTestApplication.Start(TestServices.StyledWindow.With(windowingPlatform:
+ new MockWindowingPlatform(null,
+ x =>
+ {
+ return MockWindowingPlatform.CreatePopupMock(x).Object;
+ }),
+ focusManager: new FocusManager(),
+ keyboardDevice: () => new KeyboardDevice()));
+ }
+
+ private Window PreparedWindow(object content = null)
+ {
+ var w = new Window { Content = content };
+ w.ApplyTemplate();
+ return w;
+ }
+ }
+}
diff --git a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs
index 9f09c19110..1eec1d84c1 100644
--- a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Reactive.Linq;
using System.Threading.Tasks;
using Avalonia.Controls.Presenters;
@@ -56,7 +57,44 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal("123", target1.SelectedText);
}
}
-
+
+ [Fact]
+ public void Opening_Context_Flyout_Does_not_Lose_Selection()
+ {
+ using (UnitTestApplication.Start(FocusServices))
+ {
+ var target1 = new TextBox
+ {
+ Template = CreateTemplate(),
+ Text = "1234",
+ ContextFlyout = new MenuFlyout
+ {
+ Items = new List