Browse Source

Merge branch 'master' into pointerover-fixes

pull/8918/head
Max Katz 4 years ago
committed by GitHub
parent
commit
fe341e00b4
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      native/Avalonia.Native/src/OSX/WindowImpl.mm
  2. 27
      samples/IntegrationTestApp/MacOSIntegration.cs
  3. 13
      samples/IntegrationTestApp/MainWindow.axaml.cs
  4. 8
      samples/IntegrationTestApp/ShowWindowTest.axaml
  5. 28
      samples/IntegrationTestApp/ShowWindowTest.axaml.cs
  6. 15
      src/Avalonia.Base/Data/Core/Plugins/MethodAccessorPlugin.cs
  7. 1
      src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs
  8. 1
      src/Avalonia.Controls/TreeView.cs
  9. 19
      src/Avalonia.Controls/TreeViewItem.cs
  10. 3
      src/Avalonia.X11/NativeDialogs/Gtk.cs
  11. 1
      src/Linux/Avalonia.LinuxFramebuffer/Input/EvDev/EvDevBackend.cs
  12. 3
      src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs
  13. 3
      src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs
  14. 2
      tests/Avalonia.IntegrationTests.Appium/WindowTests.cs
  15. 89
      tests/Avalonia.IntegrationTests.Appium/WindowTests_MacOS.cs
  16. 13
      tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_Method.cs
  17. 26
      tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_Method.cs

2
native/Avalonia.Native/src/OSX/WindowImpl.mm

@ -91,8 +91,6 @@ HRESULT WindowImpl::SetParent(IAvnWindow *parent) {
if(_parent != nullptr) if(_parent != nullptr)
{ {
_parent->_children.remove(this); _parent->_children.remove(this);
_parent->BringToFront();
} }
auto cparent = dynamic_cast<WindowImpl *>(parent); auto cparent = dynamic_cast<WindowImpl *>(parent);

27
samples/IntegrationTestApp/MacOSIntegration.cs

@ -0,0 +1,27 @@
using System;
using System.Runtime.InteropServices;
using Avalonia.Controls;
namespace IntegrationTestApp
{
public static class MacOSIntegration
{
[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "sel_registerName")]
private static extern IntPtr GetHandle(string name);
[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSend")]
private static extern long Int64_objc_msgSend(IntPtr receiver, IntPtr selector);
private static readonly IntPtr s_orderedIndexSelector;
static MacOSIntegration()
{
s_orderedIndexSelector = GetHandle("orderedIndex");;
}
public static long GetOrderedIndex(Window window)
{
return Int64_objc_msgSend(window.PlatformImpl!.Handle.Handle, s_orderedIndexSelector);
}
}
}

13
samples/IntegrationTestApp/MainWindow.axaml.cs

@ -1,11 +1,13 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Avalonia; using Avalonia;
using Avalonia.Automation;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.VisualTree; using Avalonia.VisualTree;
using Microsoft.CodeAnalysis;
namespace IntegrationTestApp namespace IntegrationTestApp
{ {
@ -63,6 +65,17 @@ namespace IntegrationTestApp
WindowStartupLocation = (WindowStartupLocation)locationComboBox.SelectedIndex, WindowStartupLocation = (WindowStartupLocation)locationComboBox.SelectedIndex,
}; };
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime lifetime)
{
// Make sure the windows have unique names and AutomationIds.
var existing = lifetime.Windows.OfType<ShowWindowTest>().Count();
if (existing > 0)
{
AutomationProperties.SetAutomationId(window, window.Name + (existing + 1));
window.Title += $" {existing + 1}";
}
}
if (size.HasValue) if (size.HasValue)
{ {
window.Width = size.Value.Width; window.Width = size.Value.Width;

8
samples/IntegrationTestApp/ShowWindowTest.axaml

@ -3,7 +3,7 @@
x:Class="IntegrationTestApp.ShowWindowTest" x:Class="IntegrationTestApp.ShowWindowTest"
Name="SecondaryWindow" Name="SecondaryWindow"
Title="Show Window Test"> Title="Show Window Test">
<Grid ColumnDefinitions="Auto,Auto" RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto"> <Grid ColumnDefinitions="Auto,Auto" RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
<Label Grid.Column="0" Grid.Row="1">Client Size</Label> <Label Grid.Column="0" Grid.Row="1">Client Size</Label>
<TextBox Name="ClientSize" Grid.Column="1" Grid.Row="1" IsReadOnly="True" <TextBox Name="ClientSize" Grid.Column="1" Grid.Row="1" IsReadOnly="True"
Text="{Binding ClientSize, Mode=OneWay}"/> Text="{Binding ClientSize, Mode=OneWay}"/>
@ -31,6 +31,10 @@
<ComboBoxItem>Maximized</ComboBoxItem> <ComboBoxItem>Maximized</ComboBoxItem>
<ComboBoxItem>Fullscreen</ComboBoxItem> <ComboBoxItem>Fullscreen</ComboBoxItem>
</ComboBox> </ComboBox>
<Button Name="HideButton" Grid.Row="8" Command="{Binding $parent[Window].Hide}">Hide</Button>
<Label Grid.Column="0" Grid.Row="8">Order (mac)</Label>
<TextBox Name="Order" Grid.Column="1" Grid.Row="8" IsReadOnly="True"/>
<Button Name="HideButton" Grid.Row="9" Command="{Binding $parent[Window].Hide}">Hide</Button>
</Grid> </Grid>
</Window> </Window>

28
samples/IntegrationTestApp/ShowWindowTest.axaml.cs

@ -1,21 +1,32 @@
using System; using System;
using System.Runtime.InteropServices;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.Rendering; using Avalonia.Threading;
namespace IntegrationTestApp namespace IntegrationTestApp
{ {
public class ShowWindowTest : Window public class ShowWindowTest : Window
{ {
private readonly DispatcherTimer? _timer;
private readonly TextBox? _orderTextBox;
public ShowWindowTest() public ShowWindowTest()
{ {
InitializeComponent(); InitializeComponent();
DataContext = this; DataContext = this;
PositionChanged += (s, e) => this.GetControl<TextBox>("Position").Text = $"{Position}"; PositionChanged += (s, e) => this.GetControl<TextBox>("Position").Text = $"{Position}";
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
_orderTextBox = this.GetControl<TextBox>("Order");
_timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(250) };
_timer.Tick += TimerOnTick;
_timer.Start();
}
}
private void InitializeComponent() private void InitializeComponent()
{ {
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
@ -36,5 +47,16 @@ namespace IntegrationTestApp
ownerRect.Text = $"{owner.Position}, {PixelSize.FromSize(owner.FrameSize!.Value, scaling)}"; ownerRect.Text = $"{owner.Position}, {PixelSize.FromSize(owner.FrameSize!.Value, scaling)}";
} }
} }
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
_timer?.Stop();
}
private void TimerOnTick(object? sender, EventArgs e)
{
_orderTextBox!.Text = MacOSIntegration.GetOrderedIndex(this).ToString();
}
} }
} }

15
src/Avalonia.Base/Data/Core/Plugins/MethodAccessorPlugin.cs

@ -55,13 +55,20 @@ namespace Avalonia.Data.Core.Plugins
var methods = type.GetMethods(bindingFlags); var methods = type.GetMethods(bindingFlags);
foreach (MethodInfo methodInfo in methods) foreach (var methodInfo in methods)
{ {
if (methodInfo.Name == methodName) if (methodInfo.Name == methodName)
{ {
found = methodInfo; var parameters = methodInfo.GetParameters();
if (parameters.Length == 1 && parameters[0].ParameterType == typeof(object))
break; {
found = methodInfo;
break;
}
else if (parameters.Length == 0)
{
found = methodInfo;
}
} }
} }

1
src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs

@ -138,7 +138,6 @@ namespace Avalonia.Controls
private bool _clipValueToMinMax; private bool _clipValueToMinMax;
private bool _isSyncingTextAndValueProperties; private bool _isSyncingTextAndValueProperties;
private bool _isTextChangedFromUI; private bool _isTextChangedFromUI;
private CultureInfo? _cultureInfo;
private NumberStyles _parsingNumberStyle = NumberStyles.Any; private NumberStyles _parsingNumberStyle = NumberStyles.Any;
private NumberFormatInfo? _numberFormat; private NumberFormatInfo? _numberFormat;

1
src/Avalonia.Controls/TreeView.cs

@ -495,6 +495,7 @@ namespace Avalonia.Controls
break; break;
case NavigationDirection.Down: case NavigationDirection.Down:
case NavigationDirection.Right:
if (from?.IsExpanded == true && intoChildren && from.ItemCount > 0) if (from?.IsExpanded == true && intoChildren && from.ItemCount > 0)
{ {
result = (TreeViewItem)from.ItemContainerGenerator.ContainerFromIndex(0)!; result = (TreeViewItem)from.ItemContainerGenerator.ContainerFromIndex(0)!;

19
src/Avalonia.Controls/TreeViewItem.cs

@ -157,17 +157,26 @@ namespace Avalonia.Controls
switch (e.Key) switch (e.Key)
{ {
case Key.Right: case Key.Right:
if (Items != null && Items.Cast<object>().Any()) if (Items != null && Items.Cast<object>().Any() && !IsExpanded)
{ {
IsExpanded = true; IsExpanded = true;
e.Handled = true;
} }
e.Handled = true;
break; break;
case Key.Left: case Key.Left:
IsExpanded = false; if (Items is not null && Items.Cast<object>().Any() && IsExpanded)
e.Handled = true; {
if (IsFocused)
{
IsExpanded = false;
}
else
{
FocusManager.Instance?.Focus(this, NavigationMethod.Directional);
}
e.Handled = true;
}
break; break;
} }
} }

3
src/Avalonia.X11/NativeDialogs/Gtk.cs

@ -258,9 +258,6 @@ namespace Avalonia.X11.NativeDialogs
public static IntPtr GetForeignWindow(IntPtr xid) => gdk_x11_window_foreign_new_for_display(s_display, xid); public static IntPtr GetForeignWindow(IntPtr xid) => gdk_x11_window_foreign_new_for_display(s_display, xid);
static object s_startGtkLock = new();
static Task<bool> s_startGtkTask;
public static Task<bool> StartGtk() public static Task<bool> StartGtk()
{ {
return StartGtkCore(); return StartGtkCore();

1
src/Linux/Avalonia.LinuxFramebuffer/Input/EvDev/EvDevBackend.cs

@ -13,7 +13,6 @@ namespace Avalonia.LinuxFramebuffer.Input.EvDev
private readonly EvDevDeviceDescription[] _deviceDescriptions; private readonly EvDevDeviceDescription[] _deviceDescriptions;
private readonly List<EvDevDeviceHandler> _handlers = new List<EvDevDeviceHandler>(); private readonly List<EvDevDeviceHandler> _handlers = new List<EvDevDeviceHandler>();
private int _epoll; private int _epoll;
private bool _isQueueHandlerTriggered;
private object _lock = new object(); private object _lock = new object();
private Action<RawInputEventArgs> _onInput; private Action<RawInputEventArgs> _onInput;
private IInputRoot _inputRoot; private IInputRoot _inputRoot;

3
src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs

@ -46,7 +46,6 @@ namespace Avalonia.Web.Blazor
private GRGlInterface? _glInterface; private GRGlInterface? _glInterface;
private const SKColorType ColorType = SKColorType.Rgba8888; private const SKColorType ColorType = SKColorType.Rgba8888;
private bool _initialised;
private bool _useGL; private bool _useGL;
private bool _inputElementFocused; private bool _inputElementFocused;
@ -307,8 +306,6 @@ namespace Avalonia.Web.Blazor
_interop.SetCanvasSize((int)(_canvasSize.Width * _dpi), (int)(_canvasSize.Height * _dpi)); _interop.SetCanvasSize((int)(_canvasSize.Width * _dpi), (int)(_canvasSize.Height * _dpi));
_initialised = true;
Threading.Dispatcher.UIThread.Post(async () => Threading.Dispatcher.UIThread.Post(async () =>
{ {
_interop.RequestAnimationFrame(true); _interop.RequestAnimationFrame(true);

3
src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs

@ -17,7 +17,6 @@ namespace Avalonia.Win32.Input
private bool _active; private bool _active;
private bool _showCompositionWindow; private bool _showCompositionWindow;
private Imm32CaretManager _caretManager = new(); private Imm32CaretManager _caretManager = new();
private bool _showCandidateList;
private ushort _langId; private ushort _langId;
private const int _caretMargin = 1; private const int _caretMargin = 1;
@ -32,7 +31,6 @@ namespace Avalonia.Win32.Input
_active = false; _active = false;
_langId = PRIMARYLANGID(LGID(HKL)); _langId = PRIMARYLANGID(LGID(HKL));
_showCompositionWindow = true; _showCompositionWindow = true;
_showCandidateList = true;
IsComposing = false; IsComposing = false;
} }
@ -50,7 +48,6 @@ namespace Avalonia.Win32.Input
_active = false; _active = false;
_langId = 0; _langId = 0;
_showCompositionWindow = false; _showCompositionWindow = false;
_showCandidateList = false;
IsComposing = false; IsComposing = false;
} }

2
tests/Avalonia.IntegrationTests.Appium/WindowTests.cs

@ -230,7 +230,7 @@ namespace Avalonia.IntegrationTests.Appium
PixelRect.Parse(_session.FindElementByAccessibilityId("ScreenRect").Text), PixelRect.Parse(_session.FindElementByAccessibilityId("ScreenRect").Text),
double.Parse(_session.FindElementByAccessibilityId("Scaling").Text)); double.Parse(_session.FindElementByAccessibilityId("Scaling").Text));
} }
catch (OpenQA.Selenium.NoSuchElementException e) when (retry++ < 3) catch (OpenQA.Selenium.NoSuchElementException) when (retry++ < 3)
{ {
// MacOS sometimes seems to need a bit of time to get itself back in order after switching out // MacOS sometimes seems to need a bit of time to get itself back in order after switching out
// of fullscreen. // of fullscreen.

89
tests/Avalonia.IntegrationTests.Appium/WindowTests_MacOS.cs

@ -30,7 +30,7 @@ namespace Avalonia.IntegrationTests.Appium
tab.Click(); tab.Click();
return; return;
} }
catch (WebDriverException e) when (retry++ < 3) catch (WebDriverException) when (retry++ < 3)
{ {
// MacOS sometimes seems to need a bit of time to get itself back in order after switching out // MacOS sometimes seems to need a bit of time to get itself back in order after switching out
// of fullscreen. // of fullscreen.
@ -49,19 +49,16 @@ namespace Avalonia.IntegrationTests.Appium
{ {
mainWindow.Click(); mainWindow.Click();
var windows = _session.FindElements(By.XPath("XCUIElementTypeWindow")); var secondaryWindowIndex = GetWindowOrder("SecondaryWindow");
var mainWindowIndex = GetWindowOrder(windows, "MainWindow");
var secondaryWindowIndex = GetWindowOrder(windows, "SecondaryWindow");
Assert.Equal(0, secondaryWindowIndex); Assert.Equal(1, secondaryWindowIndex);
Assert.Equal(1, mainWindowIndex);
} }
} }
[PlatformFact(TestPlatforms.MacOS)] [PlatformFact(TestPlatforms.MacOS)]
public void WindowOrder_Modal_Dialog_Stays_InFront_Of_Parent_When_Clicking_Resize_Grip() public void WindowOrder_Modal_Dialog_Stays_InFront_Of_Parent_When_Clicking_Resize_Grip()
{ {
var mainWindow = FindWindow(_session, "MainWindow"); var mainWindow = GetWindow("MainWindow");
using (OpenWindow(new PixelSize(200, 100), ShowWindowMode.Modal, WindowStartupLocation.Manual)) using (OpenWindow(new PixelSize(200, 100), ShowWindowMode.Modal, WindowStartupLocation.Manual))
{ {
@ -70,24 +67,21 @@ namespace Avalonia.IntegrationTests.Appium
.ClickAndHold() .ClickAndHold()
.Perform(); .Perform();
var windows = _session.FindElements(By.XPath("XCUIElementTypeWindow")); var secondaryWindowIndex = GetWindowOrder("SecondaryWindow");
var mainWindowIndex = GetWindowOrder(windows, "MainWindow");
var secondaryWindowIndex = GetWindowOrder(windows, "SecondaryWindow");
new Actions(_session) new Actions(_session)
.MoveToElement(mainWindow, 100, 1) .MoveToElement(mainWindow, 100, 1)
.Release() .Release()
.Perform(); .Perform();
Assert.Equal(0, secondaryWindowIndex); Assert.Equal(1, secondaryWindowIndex);
Assert.Equal(1, mainWindowIndex);
} }
} }
[PlatformFact(TestPlatforms.MacOS)] [PlatformFact(TestPlatforms.MacOS)]
public void WindowOrder_Modal_Dialog_Stays_InFront_Of_Parent_When_In_Fullscreen() public void WindowOrder_Modal_Dialog_Stays_InFront_Of_Parent_When_In_Fullscreen()
{ {
var mainWindow = FindWindow(_session, "MainWindow"); var mainWindow = GetWindow("MainWindow");
var buttons = mainWindow.GetChromeButtons(); var buttons = mainWindow.GetChromeButtons();
buttons.maximize.Click(); buttons.maximize.Click();
@ -98,14 +92,8 @@ namespace Avalonia.IntegrationTests.Appium
{ {
using (OpenWindow(new PixelSize(200, 100), ShowWindowMode.Modal, WindowStartupLocation.Manual)) using (OpenWindow(new PixelSize(200, 100), ShowWindowMode.Modal, WindowStartupLocation.Manual))
{ {
var windows = _session.FindElements(By.XPath("XCUIElementTypeWindow")); var secondaryWindowIndex = GetWindowOrder("SecondaryWindow");
var mainWindowIndex = GetWindowOrder(windows, "MainWindow"); Assert.Equal(1, secondaryWindowIndex);
var secondaryWindowIndex = GetWindowOrder(windows, "SecondaryWindow");
Assert.Equal(0, secondaryWindowIndex);
Assert.Equal(1, mainWindowIndex);
Thread.Sleep(5000);
} }
} }
finally finally
@ -122,13 +110,8 @@ namespace Avalonia.IntegrationTests.Appium
using (OpenWindow(new PixelSize(200, 100), ShowWindowMode.Owned, WindowStartupLocation.Manual)) using (OpenWindow(new PixelSize(200, 100), ShowWindowMode.Owned, WindowStartupLocation.Manual))
{ {
mainWindow.Click(); mainWindow.Click();
var secondaryWindowIndex = GetWindowOrder("SecondaryWindow");
var windows = _session.FindElements(By.XPath("XCUIElementTypeWindow")); Assert.Equal(1, secondaryWindowIndex);
var mainWindowIndex = GetWindowOrder(windows, "MainWindow");
var secondaryWindowIndex = GetWindowOrder(windows, "SecondaryWindow");
Assert.Equal(0, secondaryWindowIndex);
Assert.Equal(1, mainWindowIndex);
} }
} }
@ -141,22 +124,35 @@ namespace Avalonia.IntegrationTests.Appium
{ {
mainWindow.Click(); mainWindow.Click();
var windows = _session.FindElements(By.XPath("XCUIElementTypeWindow")); var secondaryWindowIndex = GetWindowOrder("SecondaryWindow");
var mainWindowIndex = GetWindowOrder(windows, "MainWindow");
var secondaryWindowIndex = GetWindowOrder(windows, "SecondaryWindow");
Assert.Equal(1, secondaryWindowIndex); Assert.Equal(2, secondaryWindowIndex);
Assert.Equal(0, mainWindowIndex);
var sendToBack = _session.FindElementByAccessibilityId("SendToBack"); var sendToBack = _session.FindElementByAccessibilityId("SendToBack");
sendToBack.Click(); sendToBack.Click();
} }
} }
[PlatformFact(TestPlatforms.MacOS)]
public void WindowOrder_Owned_Is_Correct_After_Closing_Window()
{
using (OpenWindow(new PixelSize(300, 500), ShowWindowMode.Owned, WindowStartupLocation.CenterOwner))
{
// Open a second child window, and close it.
using (OpenWindow(new PixelSize(200, 200), ShowWindowMode.Owned, WindowStartupLocation.CenterOwner))
{
}
var secondaryWindowIndex = GetWindowOrder("SecondaryWindow");
Assert.Equal(1, secondaryWindowIndex);
}
}
[PlatformFact(TestPlatforms.MacOS)] [PlatformFact(TestPlatforms.MacOS)]
public void Parent_Window_Has_Disabled_ChromeButtons_When_Modal_Dialog_Shown() public void Parent_Window_Has_Disabled_ChromeButtons_When_Modal_Dialog_Shown()
{ {
var window = FindWindow(_session, "MainWindow"); var window = GetWindow("MainWindow");
var (closeButton, miniaturizeButton, zoomButton) = window.GetChromeButtons(); var (closeButton, miniaturizeButton, zoomButton) = window.GetChromeButtons();
Assert.True(closeButton.Enabled); Assert.True(closeButton.Enabled);
@ -176,7 +172,7 @@ namespace Avalonia.IntegrationTests.Appium
{ {
using (OpenWindow(new PixelSize(200, 100), ShowWindowMode.Modal, WindowStartupLocation.CenterOwner)) using (OpenWindow(new PixelSize(200, 100), ShowWindowMode.Modal, WindowStartupLocation.CenterOwner))
{ {
var secondaryWindow = FindWindow(_session, "SecondaryWindow"); var secondaryWindow = GetWindow("SecondaryWindow");
var (closeButton, miniaturizeButton, zoomButton) = secondaryWindow.GetChromeButtons(); var (closeButton, miniaturizeButton, zoomButton) = secondaryWindow.GetChromeButtons();
Assert.True(closeButton.Enabled); Assert.True(closeButton.Enabled);
@ -192,7 +188,7 @@ namespace Avalonia.IntegrationTests.Appium
{ {
using (OpenWindow(new PixelSize(200, 100), mode, WindowStartupLocation.Manual)) using (OpenWindow(new PixelSize(200, 100), mode, WindowStartupLocation.Manual))
{ {
var secondaryWindow = FindWindow(_session, "SecondaryWindow"); var secondaryWindow = GetWindow("SecondaryWindow");
var (_, miniaturizeButton, _) = secondaryWindow.GetChromeButtons(); var (_, miniaturizeButton, _) = secondaryWindow.GetChromeButtons();
miniaturizeButton.Click(); miniaturizeButton.Click();
@ -220,7 +216,7 @@ namespace Avalonia.IntegrationTests.Appium
// causes Appium to think it's a different window. // causes Appium to think it's a different window.
OpenWindow(null, ShowWindowMode.Owned, WindowStartupLocation.Manual); OpenWindow(null, ShowWindowMode.Owned, WindowStartupLocation.Manual);
var secondaryWindow = FindWindow(_session, "SecondaryWindow"); var secondaryWindow = GetWindow("SecondaryWindow");
var hideButton = secondaryWindow.FindElementByAccessibilityId("HideButton"); var hideButton = secondaryWindow.FindElementByAccessibilityId("HideButton");
hideButton.Click(); hideButton.Click();
@ -236,7 +232,7 @@ namespace Avalonia.IntegrationTests.Appium
_session.FindElementByAccessibilityId("RestoreAll").Click(); _session.FindElementByAccessibilityId("RestoreAll").Click();
// Close the window manually. // Close the window manually.
secondaryWindow = FindWindow(_session, "SecondaryWindow"); secondaryWindow = GetWindow("SecondaryWindow");
secondaryWindow.GetChromeButtons().close.Click(); secondaryWindow.GetChromeButtons().close.Click();
} }
@ -259,18 +255,19 @@ namespace Avalonia.IntegrationTests.Appium
return showButton.OpenWindowWithClick(); return showButton.OpenWindowWithClick();
} }
private static int GetWindowOrder(IReadOnlyCollection<AppiumWebElement> elements, string identifier) private AppiumWebElement GetWindow(string identifier)
{ {
return elements.TakeWhile(x => // The Avalonia a11y tree currently exposes two nested Window elements, this is a bug and should be fixed
x.FindElementByXPath("XCUIElementTypeWindow")?.GetAttribute("identifier") != identifier).Count(); // but in the meantime use the `parent::' selector to return the parent "real" window.
return _session.FindElementByXPath(
$"XCUIElementTypeWindow//*[@identifier='{identifier}']/parent::XCUIElementTypeWindow");
} }
private static AppiumWebElement FindWindow(AppiumDriver<AppiumWebElement> session, string identifier) private int GetWindowOrder(string identifier)
{ {
var windows = session.FindElementsByXPath("XCUIElementTypeWindow"); var window = GetWindow(identifier);
return windows.First(x => var order = window.FindElementByXPath("//*[@identifier='Order']");
x.FindElementsByXPath("XCUIElementTypeWindow") return int.Parse(order.Text);
.Any(y => y.GetAttribute("identifier") == identifier));
} }
public enum ShowWindowMode public enum ShowWindowMode

13
tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_Method.cs

@ -19,12 +19,9 @@ namespace Avalonia.Markup.UnitTests.Parsers
public int MethodWithReturn() => 0; public int MethodWithReturn() => 0;
public int MethodWithReturnAndParameters(int i) => i; public int MethodWithReturnAndParameter(object i) => (int)i;
public static void StaticMethod() { } public static void StaticMethod() { }
public static void ManyParameters(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9) { }
public static int ManyParametersWithReturnType(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) => 1;
} }
[Fact] [Fact]
@ -42,10 +39,8 @@ namespace Avalonia.Markup.UnitTests.Parsers
[Theory] [Theory]
[InlineData(nameof(TestObject.MethodWithoutReturn), typeof(Action))] [InlineData(nameof(TestObject.MethodWithoutReturn), typeof(Action))]
[InlineData(nameof(TestObject.MethodWithReturn), typeof(Func<int>))] [InlineData(nameof(TestObject.MethodWithReturn), typeof(Func<int>))]
[InlineData(nameof(TestObject.MethodWithReturnAndParameters), typeof(Func<int, int>))] [InlineData(nameof(TestObject.MethodWithReturnAndParameter), typeof(Func<object, int>))]
[InlineData(nameof(TestObject.StaticMethod), typeof(Action))] [InlineData(nameof(TestObject.StaticMethod), typeof(Action))]
[InlineData(nameof(TestObject.ManyParameters), typeof(Action<int, int, int, int, int, int, int, int, int>))]
[InlineData(nameof(TestObject.ManyParametersWithReturnType), typeof(Func<int, int, int, int, int, int, int, int, int>))]
public async Task Should_Get_Method_WithCorrectDelegateType(string methodName, Type expectedType) public async Task Should_Get_Method_WithCorrectDelegateType(string methodName, Type expectedType)
{ {
var data = new TestObject(); var data = new TestObject();
@ -61,10 +56,10 @@ namespace Avalonia.Markup.UnitTests.Parsers
public async Task Can_Call_Method_Returned_From_Observer() public async Task Can_Call_Method_Returned_From_Observer()
{ {
var data = new TestObject(); var data = new TestObject();
var observer = ExpressionObserverBuilder.Build(data, nameof(TestObject.MethodWithReturnAndParameters)); var observer = ExpressionObserverBuilder.Build(data, nameof(TestObject.MethodWithReturnAndParameter));
var result = await observer.Take(1); var result = await observer.Take(1);
var callback = (Func<int, int>)result; var callback = (Func<object, int>)result;
Assert.Equal(1, callback(1)); Assert.Equal(1, callback(1));

26
tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_Method.cs

@ -141,6 +141,28 @@ namespace Avalonia.Markup.Xaml.UnitTests.Data
} }
} }
[Fact]
public void Binding_Method_Preserves_Correct_Order()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.Xaml;assembly=Avalonia.Markup.Xaml.UnitTests'>
<Button Name='button' Command='{Binding Method3}' CommandParameter='5'/>
</Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var button = window.FindControl<Button>("button");
var vm = new ViewModel();
button.DataContext = vm;
window.ApplyTemplate();
PerformClick(button);
Assert.Equal("Called Method with parameter of object type. Argument value is 5", vm.Value);
}
}
[Fact] [Fact]
public void Binding_Method_To_Command_Collected() public void Binding_Method_To_Command_Collected()
{ {
@ -198,8 +220,10 @@ namespace Avalonia.Markup.Xaml.UnitTests.Data
public event PropertyChangedEventHandler PropertyChanged; public event PropertyChangedEventHandler PropertyChanged;
public string Method() => Value = "Called"; public string Method() => Value = "Called";
public string Method1(int i) => Value = $"Called {i}"; public string Method1(object i) => Value = $"Called {i}";
public string Method2(int i, int j) => Value = $"Called {i},{j}"; public string Method2(int i, int j) => Value = $"Called {i},{j}";
public string Method3() => Value = "Called";
public string Method3(object obj) => Value = $"Called Method with parameter of object type. Argument value is {obj}";
public string Value { get; private set; } = "Not called"; public string Value { get; private set; } = "Not called";
object _parameter; object _parameter;

Loading…
Cancel
Save